【发布时间】:2021-08-03 22:10:22
【问题描述】:
阅读这篇博文 - https://www.haskellforall.com/2021/05/the-trick-to-avoid-deeply-nested-error.html - 我意识到我不明白为什么“技巧”在这种情况下实际上有效:
{-# LANGUAGE NamedFieldPuns #-}
import Text.Read (readMaybe)
data Person = Person { age :: Int, alive :: Bool } deriving (Show)
example :: String -> String -> Either String Person
example ageString aliveString = do
age <- case readMaybe ageString of
Nothing -> Left "Invalid age string"
Just age -> pure age
if age < 0
then Left "Negative age"
else pure ()
alive <- case readMaybe aliveString of
Nothing -> Left "Invalid alive string"
Just alive -> pure alive
pure Person{ age, alive }
具体来说,我很难理解为什么会这样
if age < 0
then Left "Negative age"
else pure ()
类型检查。
Left "Negative age" 的类型为 Either String b
同时
pure () 的类型为 Either a ()
为什么会这样?
编辑:我将代码简化并重写为绑定操作而不是 do 块,然后看到 Will 对他已经非常出色的答案的编辑:
{-# LANGUAGE NamedFieldPuns #-}
import Text.Read (readMaybe)
newtype Person = Person { age :: Int} deriving (Show)
example :: String -> Either String Person
example ageString =
getAge ageString
>>= (\age -> checkAge age
>>= (\()-> createPerson age))
getAge :: Read b => String -> Either [Char] b
getAge ageString = case readMaybe ageString of
Nothing -> Left "Invalid age string"
Just age -> pure age
checkAge :: (Ord a, Num a) => a -> Either [Char] ()
checkAge a = if a < 0
then Left "Negative age"
else pure ()
createPerson :: Applicative f => Int -> f Person
createPerson a = pure Person { age = a }
我认为这使得通过绑定传递 () 的“技巧”更加明显 - 值取自外部范围,而 Left 确实使处理短路。
【问题讨论】:
-
很确定有一个正式的论点,即
Either String Person是Either a ()和Either String b的子类型,但我未能在我的(现已删除)答案中提出。 -
即,
Left "Invalid alive string"可以被视为Either String b类型的值,无论您为b选择什么类型,因为值中没有使用b类型的值。 -
@chepner 不必是
Either String Person,Either String anything就足够了。它在 do 块的 middle 中,没有“提取”值。 :) 所以它可以作为守卫。 -
@WillNess 但是
>>=对Either a的定义意味着它将被用作返回值。pure ()只进行类型检查,因为它不是do块中的最后一个表达式。 -
@chepner 啊,是的,但是no:它是不是
l@(Left x) >>= _ = l!右边的Left x确实创建了一个另一个 类型的new 值,Either String Person确实如此! (使用相同的x,因此它必须是Either String _(非正式写作))。有两种类型不是同一个值,而是两个值。
标签: haskell types typechecking do-notation