我对Haskell和函数式编程还很陌生,最近我正在学习有关函子,应用程序和Monad的知识。虽然我似乎了解一些基础知识,但是当某些参数的类型更改为Applicative时,我很难找出最好/最惯用的方法应用函数参数。考虑下面的简单代码:
myfun :: Int -> Int -> Int -> Int
myfun a b c = a + b + c -- lets pretend this does something more complicated
a = 5
b = 10
c = 20
result = myfun a b c
使用myfun
计算结果非常简单。但是,随着需求的变化,我们的输入a
,b
和c
可能会更改为Maybe Int
或[Int]
,而不是Int
。通过执行以下操作之一,我们仍然可以使用未经修改的myfun
:
result = myfun <$> a <*> b <*> c -- either like this
result = liftA3 myfun a b c -- or like that
但是,实际上,参数a
,b
和c
可能不会总是最终落在同一Applicative中,因此上述两种方法将不起作用。在不修改myfun
功能的情况下最好的方法是什么?请考虑以下a
,b
和c
的方案:
Int
,有些是Maybe Int
(应用程序的结果为Maybe Int
)Maybe Int
,有些是Either String Int
(结果可能是Maybe Int
或Either String Int
,如果有任何参数是Nothing
或Left
,则语义将使计算短路)][Int]
,有些是Maybe Int
(结果应该是Maybe [Int]
,其语义是像所有参数都是[Int]
一样计算所有可能的组合,然后将其包装在Just
内,除非在Maybies是Nothing
,在这种情况下,我们短路到Nothing
)非常感谢任何见解!
取决于您要发生的事情。可能没有任何通用的方法可以组合不同的单子。通常,当您确实需要组合不同的monad时,您可以经常(总是?)使用monad变压器,但是通常有更简单的解决方案。您提到的特定组合就是这种情况。
在所有这些特定情况下,您都可以将一个单子转换为另一个。在下文中,我将给出一些可以完成此操作的示例。
其中一些示例使用了Data.Maybe
中的函数,因此我将从以下内容开始:
import Data.Maybe
在第一个示例中不是必需的,但在第二个和第三个示例中将是必需的。
Int
,一些Maybe Int
如果您具有Int
和Maybe Int
值的组合,则解决方案很简单。只需将Int
值提升到Maybe Int
。您可以为此使用Just
或pure
。这是使用pure
的示例:
a1 = 5
b1 = Just 10
c1 = 20
result1 :: Maybe Int
result1 = myfun <$> pure a1 <*> b1 <*> pure c1
结果为Just 35
。
Maybe Int
,一些Either String Int
您可以通过将单子转换为另一种来重复此技巧。如果您有很好的Maybe Int
值可用于Either String Int
情况,则可以将String
值转换为Nothing
值。您也可以通过丢弃Either String Int
值将Maybe Int
值转换为String
值。
这是将Maybe Int
转换为Either String Int
的示例:
a2 = Just 5
b2 = Right 10
c2 = Left "Boo!"
result2 :: Either String Int
result2 = myfun <$> maybe (Left "No value") Right a2 <*> b2 <*> c2
此组合使用maybe
中的Data.Maybe
功能。结果为Left "Boo!"
。
[Int]
,一些Maybe Int
您可以使用Maybe Int
轻松地将[Int]
转换为maybeToList
:
a3 = [5, 10]
b3 = Nothing
c3 = Just 20
result3 :: [Int]
result3 = myfun <$> a3 <*> maybeToList b3 <*> maybeToList c3
这样做的结果是[]
,因为Nothing
转换为[]
,这就是Applicative
用于列表的方式。这可能不是您想要的,但我希望这些示例可以启发您提出自己想要的构图。