Haskell为Monad定义了return函数和Either



如何在Haskell中定义这种数据类型的单子?它基本上是一个萨尔萨翻译。我不知道回报是什么样子的。

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

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

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

a由对return的调用提供。(a, .

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

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

把它们放在一起就得到:

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

现在是复杂的情况,您必须处理bind (>>=)

帮助你自己打字的漏洞:在你卡住的地方写一个下划线:

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)

所以我们需要s'中的a来提供给f。模式匹配s'(重命名时):

(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,单元化结果b,并结合两个动画来提供反复看到的最终Either String (b, Context, Animation):

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

嗯,我对Salsa一无所知,但在阅读Guvante的回答预示了实施>>=的难度之后,我认为这将是一个有趣的练习。因为我不知道Salsa的上下文或动画类型是什么,我决定只是参数化它们,我认为这很好:上下文的类型对我们作为>>=的实用者来说是完全不透明的,我们只需要Animation是一个单oid:

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的实现:似乎必须有一个更好的方法来获得动画结果组合比让一个函数,然后fmapping它,但我找不到任何更漂亮的。

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

mappend anims <$> f' ctx

最新更新