-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternative instances #121
Comments
Current Alternative instance for Free is giving un-intuitive result. #!/usr/bin/env stack
{- stack
script
--resolver nightly-2017-09-07
--package free
-}
{-# LANGUAGE DeriveFunctor #-}
module Main where
import Control.Monad.Free as FreeM
import Control.Alternative.Free as FreeA
import Control.Applicative
data ActF f
= GetLine (String -> f)
| PutLine String f
deriving (Functor)
type Action a = Free (Alt ActF) a
getLn :: Action String
getLn = liftF $ liftAlt $ GetLine id
putLn :: String -> Action ()
putLn s = liftF $ liftAlt $ PutLine s ()
run p = foldFree foldAlts p
foldAlts as = runAlt interpret as
interpret (PutLine s f) = putStrLn s >> (pure f)
interpret (GetLine f) = ioError $ userError "getline failed"
(<||>) :: (Alternative f) => Free f a -> Free f a -> Free f a
(<||>) (FreeM.Pure alt1) _ = pure alt1
(<||>) (Free alt1) a2@(FreeM.Pure alt2) = FreeM.Free (alt1 <|> pure a2)
(<||>) (Free alt1) (Free alt2) = FreeM.Free (alt1 <|> alt2)
infixl 3 <||>
actualCase = ((getLn *> pure ()) <|> putLn "Actual: This should get printed")
expectedCase = ((getLn *> pure ()) <||> putLn "Expected: This should get printed")
main = do
run expectedCase
run actualCase
|
I too wonder if @michaelt's instance is sane. Here's a use-case: "upgrading" the async import Control.Alternative.Free (Alt, liftAlt, runAlt)
import Control.Concurrent.Async (Concurrently(..))
import Control.Monad.Free.Ap -- Not quite, need the alternative Alternative instance
newtype Conc a
= Conc { unConc :: Free (Alt Concurrently) a }
deriving (Functor, Applicative, Alternative, Monad)
instance MonadIO Conc where
liftIO = Conc . liftF . liftAlt . Concurrently
await :: Conc a -> IO a
await = foldFree (runConcurrently . runAlt id) . unConc For example, this would run foo :: IO Int
foo =
await $ do
i <- f1
(j, k) <- liftA2 (,) (f2 <|> f3) f4
pure (i + j + k) EDIT: Oops, I realized today that this example doesn't need the free Alternative at all. But, it would still benefit from the alternate Alternative instance for Free. |
The proposed instance, and its extension to type f ~> g = forall x. f x -> g x
newtype FF f m a = FF
{ runFF :: forall n. Monad n
=> (f ~> n) -- Any natural transformation
-> (m ~> n) -- A monad morphism
-> n a } Maybe one of you wizards can find good approaches. |
Note to email followers: my last comment has been edited. |
The following race-like Alternative instance insane? The corresponding instance for
FreeT
would makeIterT
really reduce toFreeT Identity
, sinceIterT
uses such an instance.It makes clear sense for e.g.
Free ((,)a)
where it would requirea
to be a monoid, andempty
would thus be the infinite stream ofmempty
s. When you(<|>)
them, you append the elements monoidally as you go along, but then stop when either of them stops. In the case of((->) a)
mempty
would be an infinite sink of 'a's. Maybe it's nonsense in other cases?The text was updated successfully, but these errors were encountered: