Haskell使用函数和Either定义Monad的返回值

时间:2015-09-11 18:47:30

标签: haskell monads

如何在Haskell中为这样的数据类型定义monad?它基本上是萨尔萨语翻译。我无法弄清楚回报应该是什么样的。这让我发疯了......

newtype Salsa a = Salsa {
    runSalsa :: Context -> Either String (a, Context, Animation)}

instance Monad Salsa where
    return a = Salsa $ ..........

instance Functor Salsa where
    fmap = liftM
instance Applicative Salsa where
    pure = return
    (<*>) = ap

http://ap-e2015.onlineta.org/assignments/assignment-1-salsa-interpreter.html

3 个答案:

答案 0 :(得分:6)

你需要一个Context的函数,所以使用lambda \con ->

到目前为止,你没有任何失败的东西,所以你总能成功。 Right

a的电话提供了

return(a,

Context由对lambda的调用提供。 con,

现在你必须决定要包含的动画,我猜不会。 [])。 (注意我不记得那里的确切语法,但我认为这是正确的。)

总结你得到:

return a = Salsa $ \con -> Right (a, con, [])

现在出现了一个复杂的情况,你必须处理bind(>>=)。

答案 1 :(得分:3)

帮助自己typed holes:在你陷入困境的地方写下划线:

instance Monad Salsa where
    return a = Salsa $ _

并且编译器告诉你它需要一个函数

Found hole ‘_’
  with type: Context -> Either String (a, Context, Animation)

现在你可以用

开始工作了
instance Monad Salsa where
    return a = Salsa $ \x -> _

对于>>=,请执行几乎相同的操作:

(Salsa s) >>= f = Salsa $ \x -> _ 

和编译器输出

Found hole ‘_’ with type: Either String (b, Context, Animation)
Relevant bindings include
  con :: Context
  f :: a -> Salsa b
  s :: Context -> Either String (a, Context, Animation)

因此,s是需要Context的功能,但我们的con会提供一个,将它放在一起:

(Salsa s) >>= f = Salsa $ \con -> let s' = s con in _
...
  s' :: Either String (a, Context, Animation)
    (bound at Review.hs:12:43)
  f :: a -> Salsa b (bound at Review.hs:12:19)

因此,我们需要a中的s'项来将其提供给fs'上的模式匹配(重命名时):

(Salsa salsaFunction1) >>= f = Salsa $ \context1 -> 
  let salsaResult1 = salsaFunction1 context1
   in case salsaResult1 of
        Left errorMsg                   -> Left errorMsg
        Right (a, context2, animation1) ->
           let Salsa salsaFunction2 = f a
               salsaResult2         = salsaFunction2 context2
            in _


  salsaFunction2 :: Context -> Either String (b, Context, Animation)
  animation1 :: Animation
  context2 :: Context
  a :: a
  salsaResult1 :: Either String (a, Context, Animation)
  context1 :: Context
  f :: a -> Salsa b 
  salsaFunction1 :: Context -> Either String (a, Context, Animation)

因此,我们有另一个salsaFunction2和一个未使用的context2。您已经了解了如何将它们组合在一起:在Right案例供应context3中进行另一个案例分析,使用monadic结果b并合并两个动画以提供最终Either String (b, Context, Animation)那是一次又一次地看到的:

Found hole ‘_’ with type: Either String (b, Context, Animation)

答案 2 :(得分:1)

好吧,我对萨尔萨一无所知,但在阅读了Guvante的答案,预示着实施>>=的难度,我认为这将是一个有趣的练习。由于我不知道Salsa的上下文或动画类型是什么,我决定只对它们进行参数化,我认为这些方法做得相当好:上下文的类型对于我们来说是完全不透明的>>=,我们只需要Animation成为幺半群:

newtype Salsa ctx error anim a = Salsa {
    runSalsa :: ctx -> Either error (a, ctx, anim)
    }

instance Monoid anim => Monad (Salsa ctx error anim) where
    return x = Salsa $ \ctx -> return (x, ctx, mempty)
    (Salsa m) >>= f = Salsa m'
      where m' ctx = m ctx >>= handle
            handle (x, ctx, anims) = let (Salsa f') = f x
                                         merge (a, b, c) = (a, b, mappend anims c)
                                     in merge <$> f' ctx


instance Monoid anim => Functor (Salsa ctx error anim) where
    fmap = liftM
instance Monoid anim => Applicative (Salsa ctx error anim) where
    pure = return
    (<*>) = ap

这一般我可以想象如何制作它,我仍然不完全满意handle的实现:似乎必须有更好的方法来结合动画结果而不是让一个功能,然后将其映射,但我找不到任何更漂亮的东西。

顺便说一句,我认为为这个(a, Context, Animation)组提供真实数据类型会更好,而不仅仅是一个元组。然后,您可以为其提供一个Functor实例并简化handle的实现,删除merge函数并编写

mappend anims <$> f' ctx