如何在Elm中结合结果和状态?

时间:2017-03-28 09:16:36

标签: haskell monads elm monad-transformers

我正在努力解决榆树缺少单子的问题。实现Elm状态monad的库(http://package.elm-lang.org/packages/folkertdev/elm-state/latest/State)对我帮助很大。

问题是我现在遇到了一种情况,即我只交替嵌套了Result和State类型,当时我只想拥有一个。

我尝试使用以下签名编写一个函数,但似乎不可能,因为只有在评估外部状态后才能知道内部结果。

join : Result a (State s (Result a (State s x))) -> Result a (State s x)

如果我将Result放在State中的返回值中,那么它可能会有效,但如果外部Result为Err,则会生成一个虚拟状态。

我认为正确的想法是制作既有结果也有状态的东西。熟悉Haskell monad变换器的人能解释他们如何解决这类问题或建议替代解决方案吗?

以下是出现问题的地方的粗略版本:

  generateConstraints environment value
  |> Result.map (State.map (\(value, valueC) ->
    Result.map
      (State.map2 (\this (body, bodyC) ->
        ( this
        , valueC ++ bodyC ++ [(this, body)]
        ))
      freshTypevar)
      (generateConstraints (extend environment name value) body))
  )

2 个答案:

答案 0 :(得分:3)

  

熟悉Haskell monad变换器的人能解释他们如何解决这类问题或建议替代解决方案吗?

好吧,我至少可以尝试一下。这就是你的类型直接转换为Haskell的方式:

type EffM a s x = Either a (State s x)

一个相当明显的观察结果是,它不是单变压器。 2 这就是变压器的样子:

type TransM a s x = EitherT a (State s) x

正如您所看到的,唯一的变化是T以及x在parens之外的事实。后一部分对于理解变压器方法至关重要。

核心思想是State是结果生成的一部分,无论Either是否会导致成功"或者"失败",而在你的情况下生产"失败"意味着永远不会触及状态操作。我需要更加努力地思考这在实践中意味着什么,但直观地说,变换器方法是你在使用典型的命令式代码时会想到的。

现在,当您使用这样的变换器时,join实际上是免费提供的,因为它适合Monad接口。

import Control.Monad.State
import Control.Monad.Trans.Either
import Control.Monad

type Eff e s a = EitherT e (State s) a

-- type the following in REPL

λ :t join
join :: Monad m => m (m a) -> m a

λ :t join :: Eff e s (Eff e s a) -> Eff e s a
join :: Eff e s (Eff e s a) -> Eff e s a
     :: Eff e s (Eff e s a) -> Eff e s a

-- the slightly cryptic output above means that it typechecks correctly

所以这就是Haskell 1 解决它的方法。现在,显然有可能以这种方式编写专门的EitherState类型(尽管我亲自将这两个示例中的两个翻转到StateEither - 感觉更自然),镜像各变压器的join的实施方式是什么。我不知道是否可以在Elm中专门写EitherT

1 一种可能的方法。还有其他的递归计划/ Free可能是未来几年值得关注的。效果堆叠的固有顺序结果比最初看起来更成问题。

2 它也不是Monad,至少在x不能成为Monad实例中的直接类型的意义上(因为Either显然可以在专业案例中扮演一个角色。)

答案 1 :(得分:2)

我最终写了一个同时是monad的monad。在国家被触及之前,我不得不牺牲失败的能力,因为我需要能够在失败之后失败。

SharedPreferences