Created
August 22, 2017 15:44
-
-
Save Tarmean/0bf335d460df21f516980cbc283ed680 to your computer and use it in GitHub Desktop.
Comparison of multiplicity polymorphic vs linear monad
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE InstanceSigs #-} | |
{-# LANGUAGE PolyKinds #-} | |
{-# LANGUAGE DataKinds #-} | |
{-# LANGUAGE RebindableSyntax #-} | |
{-# LANGUAGE ScopedTypeVariables #-} | |
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE TypeFamilyDependencies #-} | |
{-# LANGUAGE MagicHash #-} | |
{-# LANGUAGE UnboxedTuples #-} | |
{-# LANGUAGE TupleSections #-} | |
module LMonad where | |
import GHC.Prim | |
import qualified Unsafe.Coerce as NonLinear | |
import qualified GHC.Types as T | |
import Prelude hiding (return, (>>=), (>>), IO) | |
import qualified Prelude as P | |
import qualified System.IO as IO | |
class LMonad m where | |
return :: a ⊸ m a | |
(>>=) :: m a ⊸ (a ⊸ m b) ⊸ m b | |
(>>) :: Comonoid a => m a ⊸ m b ⊸ m b | |
ma >> mb = ma >>= \a -> next (del a) mb | |
where | |
next :: () ⊸ m b ⊸ m b | |
next () mb' = mb' | |
main :: P.IO () | |
main = toIO test | |
test :: IO () | |
test = do | |
h <- openFile "LMonadMonomorphic.hs" IO.ReadMode | |
Unrestricted content <- readLines h | |
fromIO (mapM_ putStrLn content) | |
readLines :: IO.Handle ⊸ IO [String] | |
readLines h = go h [] where | |
go :: IO.Handle ⊸ [String] -> IO [String] | |
go h' ls = do | |
(h'', Unrestricted eof) <- hIsEOF h' | |
choose eof h'' | |
(\h''' -> do | |
() <- hClose h''' | |
return (Unrestricted $ reverse ls)) | |
(\h''' -> do | |
(h'''', Unrestricted ln) <- hReadLine h''' | |
go h'''' (ln : ls)) | |
type IO a = IOL (Unrestricted a) | |
newtype IOL a = IOL (State# RealWorld -> (# State# RealWorld, a #)) | |
runIOL :: IOL a ⊸ State# RealWorld -> (# State# RealWorld, a #) | |
runIOL (IOL f) w = f w | |
instance LMonad IOL where | |
return a = IOL (\w -> (# w, a #)) | |
m >>= f = IOL (\w -> matchUnboxedLin w (runIOL m) (\w' a -> runIOL (f a) w')) | |
openFile :: FilePath -> IO.IOMode -> IOL IO.Handle | |
openFile path mode = fromIOLin (IO.openFile path mode) | |
hReadLine :: IO.Handle ⊸ IOL (IO.Handle, Unrestricted String) | |
hReadLine handle = fromIOLin (unsafeCastLinear almost handle) | |
where almost h = (h,) . Unrestricted <$> IO.hGetLine h | |
hIsEOF :: IO.Handle ⊸ IOL (IO.Handle, Unrestricted Bool) | |
hIsEOF handle = fromIOLin (unsafeCastLinear almost handle) | |
where almost h = (h,) . Unrestricted <$> IO.hIsEOF h | |
hClose :: IO.Handle ⊸ IOL () | |
hClose handle = fromIOLin (unsafeCastLinear IO.hClose handle) | |
data Unrestricted a where | |
Unrestricted :: a -> Unrestricted a | |
class Comonoid a where | |
del :: a ⊸ () | |
dup :: a ⊸ (a, a) | |
instance Comonoid () where | |
del () = () | |
dup () = ((), ()) | |
instance Comonoid (Unrestricted a) where | |
del (Unrestricted _) = () | |
dup (Unrestricted a) = (Unrestricted a, Unrestricted a) | |
choose :: Bool -> a ⊸ (a ⊸ b) -> (a ⊸ b) -> b | |
choose True v f _ = f v | |
choose False v _ f = f v | |
fromIOLin :: P.IO a ⊸ IOL (a) | |
fromIOLin (T.IO f) = IOL f | |
toIO :: IO a -> P.IO a | |
toIO (IOL f) = T.IO (\w -> case f w of (# w', Unrestricted a #) -> (# w', a #)) | |
fromIO :: P.IO a -> IOL (Unrestricted a) | |
fromIO (T.IO f) = IOL (\w -> case f w of (# w', a #) -> (# w', Unrestricted a #)) | |
unsafeCoerce :: a ⊸ b | |
unsafeCoerce = NonLinear.unsafeCoerce NonLinear.unsafeCoerce | |
unsafeCastLinear :: (a -> b) ⊸ (a ⊸ b) | |
unsafeCastLinear = unsafeCoerce | |
matchUnboxedLin :: State# RealWorld -> (State# RealWorld -> (# State# RealWorld, c #)) ⊸ (State# RealWorld -> c ⊸ (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
matchUnboxedLin = almost | |
where | |
almost :: State# RealWorld -> (State# RealWorld ⊸ (# State# RealWorld, c #)) -> (State# RealWorld ⊸ c ⊸ (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
almost a f c = case f a of | |
(# l, r #) -> c l r |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE InstanceSigs #-} | |
{-# LANGUAGE PolyKinds #-} | |
{-# LANGUAGE DataKinds #-} | |
{-# LANGUAGE RebindableSyntax #-} | |
{-# LANGUAGE ScopedTypeVariables #-} | |
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE TypeFamilyDependencies #-} | |
{-# LANGUAGE MagicHash #-} | |
{-# LANGUAGE UnboxedTuples #-} | |
{-# LANGUAGE TupleSections #-} | |
module LMonad where | |
import qualified Unsafe.Coerce as NonLinear | |
import GHC.Prim | |
import qualified GHC.Types as T | |
import Prelude hiding (return, (>>=), (>>), IO) | |
import qualified Prelude as P | |
import qualified System.IO as IO | |
class LMonad m where | |
return :: (SingI p) => Arr p a (m p a) | |
(>>=) :: (SingI p) => m p a ⊸ Arr p a (m q b) ⊸ m q b | |
(>>) :: m 'Omega a ⊸ m q b ⊸ m q b | |
a >> b = a >>= \_ -> b | |
main :: P.IO () | |
main = toIO test | |
test :: IO () | |
test = do | |
h <- openFile "LMonadPolymorphic.hs" IO.ReadMode | |
content <- readLines h | |
fromIO (mapM_ putStrLn content) | |
readLines :: IO.Handle ⊸ IOL 'Omega [String] | |
readLines h = go h [] where | |
go :: IO.Handle ⊸ [String] -> IOL 'Omega [String] | |
go h' ls = do | |
(h'', eof) <- hIsEOF h' | |
choose eof h'' | |
(\h''' -> do | |
() <- hClose h''' | |
return (reverse ls)) | |
(\h''' -> do | |
(h'''', ln) <- hReadLine h''' | |
go h'''' (ln : ls)) | |
type IO = IOL 'Omega | |
newtype IOL (p :: Multiplicity) a = IOL (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf p a #)) | |
runIOL :: IOL p a ⊸ State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf p a #) | |
runIOL (IOL f) w = f w | |
data Multiplicity = One | Omega | |
type family Arr (p :: Multiplicity) a b = result | result -> a b where | |
Arr 'One a b = a ⊸ b | |
Arr 'Omega a b = a -> b | |
instance LMonad IOL where | |
return :: forall p a. SingI p => Arr p a (IOL p a) | |
return = caseF sing where | |
caseF :: Sing p -> Arr p a (IOL p a) | |
caseF SOne = \a -> IOL (\w -> (# w, Linear a #)) | |
caseF SOmega = \a -> IOL (\w -> (# w, NotLinear a #)) | |
(>>=) :: forall p q a b. (SingI p) => IOL p a ⊸ Arr p a (IOL q b) ⊸ IOL q b | |
(>>=) = caseF sing where | |
caseF :: Sing p -> IOL p a ⊸ Arr p a (IOL q b) ⊸ IOL q b | |
caseF SOne = \m (f :: a ⊸ IOL q b) -> IOL (\w -> matchUnboxedLin w (runIOL m) (\w' a -> runIOL (f a) w')) | |
caseF SOmega = \m f -> IOL (\w -> matchUnboxed w (runIOL m) (\w' a -> runIOL (f a) w')) | |
openFile :: FilePath -> IO.IOMode -> IOL 'One IO.Handle | |
openFile path mode = fromIOLin (IO.openFile path mode) | |
hReadLine :: IO.Handle ⊸ IOL 'One (IO.Handle, String) | |
hReadLine handle = fromIOLin (unsafeCastLinear almost handle) | |
where almost h = (h,) <$> IO.hGetLine h | |
hIsEOF :: IO.Handle ⊸ IOL 'Omega (IO.Handle, Bool) | |
hIsEOF handle = fromIO (unsafeCastLinear almost handle) | |
where almost h = (h,) <$> IO.hIsEOF h | |
hClose :: IO.Handle ⊸ IOL 'Omega () | |
hClose handle = fromIO (unsafeCastLinear IO.hClose handle) | |
data Unrestricted a where | |
Unrestricted :: a -> Unrestricted a | |
data UnrestrictedIf p a where | |
Linear :: a ⊸ UnrestrictedIf 'One a | |
NotLinear :: a -> UnrestrictedIf 'Omega a | |
choose :: Bool -> a ⊸ (a ⊸ b) -> (a ⊸ b) -> b | |
choose True v f _ = f v | |
choose False v _ f = f v | |
toIO :: IO a -> P.IO a | |
toIO (IOL f) = T.IO (removeUnrestricted f) | |
fromIOLin :: P.IO a ⊸ IOL 'One a | |
fromIOLin (T.IO f) = IOL (addRestricted f) | |
fromIO :: P.IO a ⊸ IO a | |
fromIO (T.IO f) = IOL (addUnrestricted f) | |
-- Guess I will just let the bug handle this for now | |
unsafeCoerce :: a ⊸ b | |
unsafeCoerce = NonLinear.unsafeCoerce NonLinear.unsafeCoerce | |
unsafeCastLinear :: (a -> b) ⊸ (a ⊸ b) | |
unsafeCastLinear = unsafeCoerce | |
addRestricted :: (State# RealWorld ⊸ (# State# RealWorld, a #)) ⊸ State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'One a #) | |
addRestricted = almost | |
where | |
almost :: (State# RealWorld ⊸ (# State# RealWorld, a #)) -> State# RealWorld -> (# State# RealWorld, UnrestrictedIf 'One a #) | |
almost f w = case f w of | |
(# w', a #) -> (# w', Linear a #) | |
addUnrestricted :: (State# RealWorld ⊸ (# State# RealWorld, a #)) ⊸ State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'Omega a #) | |
addUnrestricted = almost | |
where | |
almost :: (State# RealWorld ⊸ (# State# RealWorld, a #)) -> State# RealWorld -> (# State# RealWorld, UnrestrictedIf 'Omega a #) | |
almost f w = case f w of | |
(# w', a #) -> (# w', NotLinear a #) | |
removeUnrestricted :: (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'Omega a #)) ⊸ State# RealWorld ⊸ (# State# RealWorld, a #) | |
removeUnrestricted = almost | |
where | |
almost :: (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'Omega a #)) -> State# RealWorld -> (# State# RealWorld, a #) | |
almost f w = case f w of | |
(# w', NotLinear a #) -> (# w', a #) | |
-- Unboxed tuples have to be case matched and case doesn't work for linear types so we | |
-- cast the function as linear and it seems to work | |
matchUnboxedLin :: State# RealWorld ⊸ (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'One c #)) ⊸ (State# RealWorld ⊸ c ⊸ (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
matchUnboxedLin = almost | |
where | |
-- can't be polymorphic over State# Realworld | |
almost :: State# RealWorld -> (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'One c #)) -> (State# RealWorld ⊸ c ⊸ (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
almost a f c = case f a of | |
(# l, Linear r #) -> c l r | |
matchUnboxed :: State# RealWorld ⊸ (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'Omega c #)) ⊸ (State# RealWorld ⊸ c -> (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
matchUnboxed = almost | |
where | |
almost :: State# RealWorld -> (State# RealWorld ⊸ (# State# RealWorld, UnrestrictedIf 'Omega c #)) -> (State# RealWorld ⊸ c -> (# State# RealWorld, d #)) ⊸ (# State# RealWorld, d #) | |
almost a f c = case f a of | |
(# l, NotLinear r #) -> c l r | |
data family Sing (a :: k) | |
data instance Sing (a :: Multiplicity) where | |
SOne :: Sing 'One | |
SOmega :: Sing 'Omega | |
class SingI (p :: k) where | |
sing :: Sing p | |
instance SingI 'One where | |
sing = SOne | |
instance SingI 'Omega where | |
sing = SOmega |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment