0

编写自定义地图功能讨论了我当前的问题后,我想我找到了解决问题的方法:

mapLift :: Monad m => (a -> b) -> [m a] -> [m b]
mapLift f = map (liftM f)

但是,当我使用它时,会出现编译器错误。我的实际代码是:

actuals     = zipWith mapLift (mapLift eval wffs) assignments

在哪里

eval :: Wff -> Assignment -> Maybe Bool
wffs :: [Either String Wff]
assignments :: [Assignment]

此代码给出以下错误消息:

Couldn't match expected type `a0 -> Wff'
            with actual type `Either String Wff'
Expected type: [a0 -> Wff]
  Actual type: [Either String Wff]
In the second argument of `mapLift', namely `wffs'
In the second argument of `zipWith', namely `(mapLift eval wffs)'

我究竟做错了什么?我想在保留任何 s 的同时eval进行映射,这些s 会在过程中的上一步给出错误消息。wffsLeft

编辑:

本来,我的想法是这样的actuals :: [Either String (Maybe Bool)]。但是,我正在考虑修改evaleval :: Wff -> Assignment -> Either String Bool,以便actuals :: [Either String Bool].

更新:

我在原来的问题中打错了字。assignments应该:

assignments :: [[Assignment]]
4

3 回答 3

2

我不确定你想要的类型是什么actuals,但我们假设它是[Either String (Maybe Bool)]. 在这种情况下,让我们从

actuals = zipWith (\ew a -> undefined) wffs assignments

哪里ew是一个Either String Wff并且a是一个Assignment。我们函数的主体应该是什么?不知何故,我们需要提升evalEither. 如果作业也在其中Either,我们可以写liftM2 eval a ew——但事实并非如此。所以,我们可以在return a这里使用 a ,或者先用 应用eval到它的Assignment参数flip,然后再提升它(或许多其他可能性):

actuals = zipWith (\ew a -> liftM (flip eval a) ew) wffs assignments

或者,让我们进一步打高尔夫球:

actuals = zipWith (\ew -> flip liftM ew . flip eval) wffs assignments

在我看来,两者都不像

\ew a -> eval <$> ew <*> return a

from<$>的另一个名称是哪里。您可以在例如“Learn you a Haskell”中了解更多信息,但基本上(相当于足够小)是.liftMControl.Applicative<$><*>f <$> ma <*> mb <*> ... <*> mnliftMn f ma mb ... mnnapplicativef a ... n

此时,您可以提取所有故障或其他任何内容。我想知道 theMaybe和 the的存在是否Either表明你可以稍微简化你的类型。例如,如果您eval可能无法评估 Wff,您可以使用 anEither而不是Maybe来记录错误。然后,错误将在它们发生的步骤中被捕获,成功将以Eithermonad 的通常方式传播:

-- eval :: Wff -> Assignment -> Either String Bool
actuals :: [Either String Bool]
actuals  = zipWith (\ew a -> ew >>= flip eval a) wffs assignments

另请参阅该errors包,它提供了一些有用的组合符(在其众多赞誉功能中)EitherMaybe.

我没有按照您建议的方式解决问题,即在压缩之前映射第一个列表。当然,你也可以这样做:

actuals  = zipWith (\ef a -> liftM ($ a) ef) (mapLift eval wffs) assignments

liftM ($ a)ef <*> return a.

这似乎有点吵 -zipWith已经是一种地图,您不妨利用这一事实。

编辑以响应 OP 的编辑:如果需要actuals :: [[Assignment]],则所需的更改很简单:

actuals :: [Either String (Maybe Bool)]
actuals = 
  concat $ zipWith (\ew -> map (liftA2 eval ew . return)) wffs assignments

这里的想法是我再次从concat $ zipWith f wffs assignments未知的形式开始f(这concat是因为[[]]类型需要以某种方式展平......如果你忘记了,类型不会完全匹配),然后向编译器查询类型of f(通过编写where f :: (); f = undefined和检查生成的错误消息),然后编写适当的函数,然后进行 eta-reduced(打高尔夫球)。GHC 7.8 将有类型漏洞,允许您获得未定义的、仍有待编写的表达式的类型,从而实现比现在更优雅的类型驱动开发 (TDD) 方式(除了 Agda 等) )。

根据我的原始帖子,将其调整为使用列表推导并具有 type [Either String Bool],这是一个有用的练习:)

于 2013-10-08T02:12:45.340 回答
2

mapLift的第一个参数是来自 的函数a -> b。您作为第一个参数传递eval :: Wff -> Assignment -> Maybe Bool。由于(->)那意味着与和a统一的关联性。这意味着 , 的下一个参数预计具有让与 统一的类型。没关系。WffbAssignment -> Maybe BoolmapLiftwffs[m Wff]mEither String

问题是结果mapLift eval wffs最终会是 type [m b]which, given what mand bunity to is [Either String (Assignment -> Maybe Bool)]。这已经向南发展了,但让我们继续关注它。

zipWith有类型(a -> b -> c) -> [a] -> [b] -> [c],我们将第一个参数作为mapLift :: (s -> t) -> [m s] -> [m t]. 这意味着zipWith最终a与函数类型统一(s -> t)b[m s][c][m t]这样

zipWith mapLift :: [s -> t] -> [[m s]] -> [[m t]]

现在让我们传入结果(mapLift eval wffs)并观看烟花。

zipWith mapLift :: [s -> t] -> [[m s]] -> [[m t]]
                (mapLift eval wffs) :: [Either String (Assignment -> Maybe Bool)]

为了继续,类型检查器必须[s -> t][Either String (Assignment -> Maybe Bool)]. 外层[]是立即分派的,但我们仍然需要找到一种方法来显示s -> t ~ Either String (Assignment -> Maybe Bool)由于 wrapping 是不可能的Either String


问题是你的类型没有对齐,因为你的语义缺少一种统一两种失败的方法——theEither StringMaybe. 幸运的是,这可以通过解包Eitherinto 函数来实现,该函数需要一个Assignment. 这是一种方法

fixEither :: Either String (Assignment -> Maybe Bool) -> (Assignment -> Maybe Bool)
fixEither (Left _)  _ = Nothing
fixEither (Right f) a = f a

最后,如果我们要尝试运行

zipWith mapLift (map fixEither $ mapLift eval wffs) assignments

还有一个问题,因为

zipWith mapLift (map fixEither $ mapLift eval wffs)
  :: [[m Assignments]] -> [[m (Maybe Bool)]]

虽然assignments :: [Assignments]不匹配。我们需要assignments至少再包裹一层[]和一些m. 此外,不清楚m你想要什么。


这意味着您可能还没有足够好地定义您的算法。

我总是建议先做一个递归算法或一个只使用“普通”的算法map来测试你是否完全理解列表和函数的各个层以及单子是如何交错的。提供的捷径非常强大fmapliftM但需要一些时间才能正确。

于 2013-10-08T02:15:44.807 回答
2

fmap或者<$>对于这种情况就足够了(假设您想要的输出类型为[Either String (Maybe Bool)]

result :: [Either String (Maybe Bool)]
result =  zipWith (<$>) [flip eval $ a | a <- assignments]  wffs
于 2013-10-08T05:46:32.000 回答