data Y f = Y (f (Y f))
type List a = Y ((,) (Either () a))
nil = Y (Left (), undefined)
cons a as = Y (Right a, as)
infixr 4 cons
numbers = 1 `cons` 2 `cons` 3 `cons` nil
-- this is like foldl
reduce f z (Y (Left (), _)) = z
reduce f z (Y (Right x, xs)) = reduce f (f z x) xs
total = reduce (+) 0 numbers
3条答案
按热度按时间c0vxltue1#
当然,
Maybe a
与Either Unit a
是同构的,但它们在语义上常常被用来表示不同的东西,这有点像返回null
和抛出NoSuchElementException
之间的区别:Nothing
/None
表示“预期”缺失某些内容,而Left e
表示在获取它时出现错误,无论是什么原因。也就是说,我们甚至可以将两者结合起来:
在这里,我们表示了丢失值(DB
NULL
)和连接、DBMS或其他方面出错的可能性(不是说没有更好的设计,但您已经明白了)。有时,边界是流动的;对于
saveHead :: [a] -> Maybe a
,我们可以说错误的预期可能性被编码在函数的意图中,而类似saveDivide
的东西可能被编码为Float -> Float -> Either FPError Float
或Float -> Float -> Maybe Float
,这取决于用例(同样,只是一些愚蠢的例子...)。如果有疑问,最好的选择可能是使用带有语义编码的自定义结果ADT(如
data QueryResult = Success String | Null | Failure DBError
),并且首选Maybe
,而不是“传统上预期的”简单情况(这是一个主观观点,但是如果您获得经验,这基本上是可以的)。hs1ihplo2#
@phg的答案很棒。我会在学习它们的时候补充一些帮助我澄清这一点的东西:
Maybe
是1(值)或无-即,有值或无值Either
是一个逻辑析取,但你总是至少有一个(值)-即,你有一个 * 或 * 另一个,但不是两个都有。Maybe
非常适合于你可能有值或者没有值的地方--例如在列表中查找一个项目。如果列表包含它,我们得到(Just x)
,否则我们得到Nothing
Either
是代码中分支的完美表示--它将以某种方式运行;Left
或Right
。我们使用助记符来记住它:Right
是 * 正确 ( 正确 *)的方式;Left
是 wrong way(错误)。当然这不是它唯一的用法,但绝对是最常见的用法。我知道这些差异一开始看起来很微妙,但实际上它们适用于非常不同的事情。
83qze16e3#
你看,我们可以把这一点放到极端,所有的乘积类型都可以用2元组来表示,所有的非递归和类型都可以用
Either
来表示。要额外表示递归类型,我们需要一个不动点类型。例如,我们可以写
(a, (b, (c,d)))
或(((a,b), c), d)
,为什么还要写4元组(a,b,c,d)
?或者为什么要有列表,当下面的工作以及?