我正在努力解决榆树缺少单子的问题。实现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))
)
答案 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