haskell中的那些运营商是什么?和< *> 我把他们排成一行:
class Evaluable e where
eval :: (Num a, Ord a) => (Ident -> Maybe a) -> (e a) -> (Either String a)
typeCheck :: (Ident -> String) -> (e a) -> Bool
instance Evaluable NExpr where
eval lookup (Plus left right) = (+) <$> eval lookup left <*> eval lookup right
答案 0 :(得分:4)
由于我是showed you these operators的人,我将简要说明我使用它们的原因。
要查看,仿函数是一种类型构造函数,它允许您使用fmap
函数将函数应用于“包装”值。在Either
类型构造函数的特定情况下(部分应用,在本例中为String
),您可以将函数应用于Right
值,但如果应用于函数则忽略该函数一个Left
值(您的错误)。它提供了一种错误传播方式,而无需检查错误。
fmap f (Right x) = Right (f x)
fmap f (Left y) = Left y
应用仿函数是类似的,除了函数本身可以像它应用的参数一样被包装。 <*>
运算符解包它的两个操作数,不像fmap
只会解包它的右操作数。
Right f <*> Right x = Right (f x)
Left f <*> _ = Left f
_ <*> Left y = Left y
通常,您不会自己包装函数:它们是使用fmap
将函数部分应用于包装值而产生的:
fmap (+) (Right 3) == Right (+ 3)
fmap (+) (Left "error") == Left "error"
因此,当我们使用Either
值时,使用<$>
(中缀fmap
)和<*>
让我们假装我们正在处理常规值,担心他们是否被Left
或Right
包裹着。 Right
值提供预期的Right
- 包装答案,并保留Left
个值。对于二元运算符,只返回第一个Left
值,但这通常就足够了。
(+) <$> Left "x undefined" <*> Left "y undefined" == Left "x undefined" <*> Left "y undefined"
== Left "x undefined"
(+) <$> Left "x undefined" <*> Right 9 == Left "x undefined" <*> Right 9
== Left "x undefined"
(+) <$> Right 3 <*> Left "y undefined" == Right (+ 3) <*> Left "y undefined"
== Left "y undefined"
(+) <$> Right 3 <*> Right 9 == Right (+3) <*> Right 9
== Right 12
最后,使用Applicative
Either String
实例让我们结合评估两个子表达式的结果,而无需显式检查eval
的递归调用是否实际成功。成功的递归调用会带来成功;任何一个呼叫中的错误都被用作顶级呼叫的相同错误。
答案 1 :(得分:3)
<$>
运算符是fmap
的中缀形式。它允许您将纯函数应用于包含在属于Functor
类的某个参数类型中的值。 <$>
的类型为(a -> b) -> f a -> f b
。
<*>
运算符与<$>
非常相似。它允许您将包含在参数类型中的函数应用于包含在相同参数类型中的值。 <*>
的类型为f (a -> b) -> f a -> f b
。
答案 2 :(得分:1)
在这种特殊情况下,它是一种结合eval
结果的方法。
如果表达式的一部分失败,那么整个表达式都会失败。
这样,它可以分离应用程序逻辑的错误处理并避免复杂的嵌套case ... of
。
要完全理解这一点,我建议先阅读有关仿函数的内容,然后再阅读适用的仿函数。
与此同时,您可以使用Maybe和Either,并使用case
表达式编写等效代码。