函数和单子操作的失败组合
Failing composition of functorial and monadic operations
我努力学习惯用语的另一个新手问题Haskell:我试图用一些验证函数组成一个智能构造函数,但我无法让类型对齐。
这是我要构建的类型:
newtype Tag = Tag { getTag :: Text }
这些是验证函数:
validateCharacters :: Text -> Maybe Text
canonicalize :: Text -> Text
validateTagLength :: Text -> Maybe Text
这是我正在尝试编写的智能构造函数:
mkTag t = Tag
<$> validateTagLength
>=> canonicalize
<$> validateCharacters t
根据我的理解,类型应该相加:canonicalize <$> validateCharacters
是 Text -> Maybe Text
类型,validateTagLength
也是,Kleisil 鱼应该组合单子函数 a -> m b
.最后,将构造函数映射到生成的 Maybe
monad 应该 return 预期的 Maybe Tag
。但是,我收到以下类型错误:
• Couldn't match type ‘Maybe Text’ with ‘Text’
Expected type: Text -> Text
Actual type: Text -> Maybe Text
• In the second argument of ‘(<$>)’, namely ‘validateTagLength’
In the first argument of ‘(>=>)’, namely
‘UnconstrainedTag <$> validateTagLength’
[...]
和
• Couldn't match expected type ‘b0 -> m c’
with actual type ‘Maybe Text’
• Possible cause: ‘(<$>)’ is applied to too many arguments
In the second argument of ‘(>=>)’, namely
‘canonicalize <$> validateCharacters t’
[...]
我的错误在哪里?我是否遗漏了一些优先规则?
您的代码使用 canonicalize <$> validateCharacters t
(注意最后的 t
!),它的类型为 Maybe Text
,因此您不能 >=>
,因为它不是函数。
你可以使用像
这样的东西
mkTag t :: Text -> Maybe Tag
mkTag t = do
canonic <- canonicalize <$> validateCharacters t
Tag <$> validateTagLength canonic
或
mkTag t :: Text -> Maybe Tag
mkTag t = Tag <$> ((canonicalize <$> validateCharacters t) >>= validateTagLength)
这可以说是可读性较差。在我看来,即使使用 =<<
来修复顺序,它看起来也比 do
变体更糟糕。
如果你真的想要一个无积分的解决方案,也许这也可以,而且还不错:
mkTag = fmap Tag . validateTagLength <=< fmap canonicalize . validateCharacters
如果你想要一个严格的从左到右的“管道”,你需要将 canonicalize
变成 Kleisli 箭头(即从 a -> b
切换到 a -> Maybe b
)用 return
合成它。您还必须首先将 validateTagLength
应用于 t
,以“启动”管道。
mkTag t = Tag <$> (validateTagLength t
>>= return . canonicalize
>>= validateCharacters)
您可以使用 >=>
使这一点成为免费的,也可以从 Tag
创建一个 Kleisli 箭头。
mkTag = validateTagLength
>=> return . canonicalize
>=> validateCharacters
>=> return . Tag
(您可能会发现定义 klift = (return .)
更具可读性,让您编写 mkTag = validateTagLength >=> klift canonicalize >=> ...
。)
不过,
没什么问题
mkTag t = do
t1 <- validateTagLength t
let t2 = canonicalize t1
t3 <- validateCharacters t2
return $ Tag t3
我努力学习惯用语的另一个新手问题Haskell:我试图用一些验证函数组成一个智能构造函数,但我无法让类型对齐。
这是我要构建的类型:
newtype Tag = Tag { getTag :: Text }
这些是验证函数:
validateCharacters :: Text -> Maybe Text
canonicalize :: Text -> Text
validateTagLength :: Text -> Maybe Text
这是我正在尝试编写的智能构造函数:
mkTag t = Tag
<$> validateTagLength
>=> canonicalize
<$> validateCharacters t
根据我的理解,类型应该相加:canonicalize <$> validateCharacters
是 Text -> Maybe Text
类型,validateTagLength
也是,Kleisil 鱼应该组合单子函数 a -> m b
.最后,将构造函数映射到生成的 Maybe
monad 应该 return 预期的 Maybe Tag
。但是,我收到以下类型错误:
• Couldn't match type ‘Maybe Text’ with ‘Text’
Expected type: Text -> Text
Actual type: Text -> Maybe Text
• In the second argument of ‘(<$>)’, namely ‘validateTagLength’
In the first argument of ‘(>=>)’, namely
‘UnconstrainedTag <$> validateTagLength’
[...]
和
• Couldn't match expected type ‘b0 -> m c’
with actual type ‘Maybe Text’
• Possible cause: ‘(<$>)’ is applied to too many arguments
In the second argument of ‘(>=>)’, namely
‘canonicalize <$> validateCharacters t’
[...]
我的错误在哪里?我是否遗漏了一些优先规则?
您的代码使用 canonicalize <$> validateCharacters t
(注意最后的 t
!),它的类型为 Maybe Text
,因此您不能 >=>
,因为它不是函数。
你可以使用像
这样的东西mkTag t :: Text -> Maybe Tag
mkTag t = do
canonic <- canonicalize <$> validateCharacters t
Tag <$> validateTagLength canonic
或
mkTag t :: Text -> Maybe Tag
mkTag t = Tag <$> ((canonicalize <$> validateCharacters t) >>= validateTagLength)
这可以说是可读性较差。在我看来,即使使用 =<<
来修复顺序,它看起来也比 do
变体更糟糕。
如果你真的想要一个无积分的解决方案,也许这也可以,而且还不错:
mkTag = fmap Tag . validateTagLength <=< fmap canonicalize . validateCharacters
如果你想要一个严格的从左到右的“管道”,你需要将 canonicalize
变成 Kleisli 箭头(即从 a -> b
切换到 a -> Maybe b
)用 return
合成它。您还必须首先将 validateTagLength
应用于 t
,以“启动”管道。
mkTag t = Tag <$> (validateTagLength t
>>= return . canonicalize
>>= validateCharacters)
您可以使用 >=>
使这一点成为免费的,也可以从 Tag
创建一个 Kleisli 箭头。
mkTag = validateTagLength
>=> return . canonicalize
>=> validateCharacters
>=> return . Tag
(您可能会发现定义 klift = (return .)
更具可读性,让您编写 mkTag = validateTagLength >=> klift canonicalize >=> ...
。)
不过,
没什么问题mkTag t = do
t1 <- validateTagLength t
let t2 = canonicalize t1
t3 <- validateCharacters t2
return $ Tag t3