除了错误处理之外,您不会看到Maybe List
,因为列表本身有点Maybe
:他们有自己的“Nothing
”:{{1} }和他们自己的“[]
”:Just
。
我使用Maybe和函数编写了一个列表类型,将标准转换为“实验”列表。 (:)
。
toStd . toExp == id
你怎么看待它,作为减少重复的尝试,概括一下?
树也可以使用这些列表定义:
data List a = List a (Maybe (List a))
deriving (Eq, Show, Read)
toExp [] = Nothing
toExp (x:xs) = Just (List x (toExp xs))
toStd Nothing = []
toStd (Just (List x xs)) = x : (toStd xs)
但我还没有测试过最后一段代码。
答案 0 :(得分:9)
您可以在Haskell中以多种方式定义列表。例如,作为函数:
{-# LANGUAGE RankNTypes #-}
newtype List a = List { runList :: forall b. (a -> b -> b) -> b -> b }
nil :: List a
nil = List (\_ z -> z )
cons :: a -> List a -> List a
cons x xs = List (\f z -> f x (runList xs f z))
isNil :: List a -> Bool
isNil xs = runList xs (\x xs -> False) True
head :: List a -> a
head xs = runList xs (\x xs -> x) (error "empty list")
tail :: List a -> List a
tail xs | isNil xs = error "empty list"
tail xs = fst (runList xs go (nil, nil))
where go x (xs, xs') = (xs', cons x xs)
foldr :: (a -> b -> b) -> b -> List a -> b
foldr f z xs = runList xs f z
这个实现的技巧是列表被表示为执行列表元素折叠的函数:
fromNative :: [a] -> List a
fromNative xs = List (\f z -> foldr f z xs)
toNative :: List a -> [a]
toNative xs = runList xs (:) []
在任何情况下,真正重要的是类型及其操作遵循的合同(或法律)以及性能实现。基本上,任何履行合同的实现都会为您提供正确的程序,更快的实现将为您提供更快的程序。
什么是名单合同?好吧,我不打算详细地表达它,但列出服从这样的陈述:
head (x:xs) == x
tail (x:xs) == xs
[] == []
[] /= x:xs
xs == ys
和x == y
,则x:xs == y:ys
foldr f z [] == z
foldr f z (x:xs) == f x (foldr f z xs)
编辑:并将其与奥古斯丁的回答联系起来:
newtype ExpList a = ExpList (Maybe (a, ExpList a))
toExpList :: List a -> ExpList a
toExpList xs = runList xs (\x xs -> ExpList (Just (x, xs))) (ExpList Nothing)
foldExpList f z (ExpList Nothing) = z
foldExpList f z (ExpList (Just (head, taill))) = f head (foldExpList f z tail)
fromExpList :: ExpList a -> List a
fromExpList xs = List (\f z -> foldExpList f z xs)
答案 1 :(得分:9)
所有ADT都是(,)
,Either
,()
,(->)
,Void
和{{的某种组合的同构(几乎 - 见尾) 1}}其中
Mu
和data Void --using empty data decls or
newtype Void = Void Void
计算仿函数的固定点
Mu
所以例如
newtype Mu f = Mu (f (Mu f))
与
相同data [a] = [] | (a:[a])
本身与
同构data [a] = Mu (ListF a)
data ListF a f = End | Pair a f
因为
newtype ListF a f = ListF (Either () (a,f))
与
同构data Maybe a = Nothing | Just a
你有
newtype Maybe a = Maybe (Either () a)
可以在mu中内联
newtype ListF a f = ListF (Maybe (a,f))
和你的定义
data List a = List (Maybe (a,List a))
只是Mu的展开和外部Maybe的消除(对应于非空列表)
你完成了......
一些事情
使用自定义ADT可提高清晰度和类型安全性
这种普遍性很有用:参见GHC.Generic
好的,我说的几乎是同构的。这不完全是,即
data List a = List a (Maybe (List a))
在列表的hmm = List (Just undefined)
定义中没有等效值。这是因为Haskell数据类型是coinductive,并且已经a point of criticism of the lazy evaluation model。您可以通过仅使用严格的总和和产品(以及通过值函数调用)以及添加特殊的“Lazy”数据构造函数来解决这些问题
[a] = [] | (a:[a])
然后可以忠实地编码所有同构。
答案 2 :(得分:7)
您可以按Maybe
来定义列表,但不是这样。您的List
类型不能为空。或者您是否打算将Maybe (List a)
替换为[a]
。这看起来很糟糕,因为它不区分列表和类型。
这样可行
newtype List a = List (Maybe (a, List a))
这有一些问题。首先使用它会比通常的列表更冗长,其次,域名与列表不同构,因为我们在那里有一对(可以是未定义的;在域中添加额外的级别)。
答案 3 :(得分:1)
如果是列表,它应该是Functor
的实例,对吧?
instance Functor List
where fmap f (List a as) = List (f a) (mapMaybeList f as)
mapMaybeList :: (a -> b) -> Maybe (List a) -> Maybe (List b)
mapMaybeList f as = fmap (fmap f) as
以下是一个问题:您可以List
Functor
的实例,但您的可能列表不是:即使Maybe
不是Functor
的实例它本身的权利,你不能直接将Maybe . List
这样的结构构建成任何实例(你需要一个包装类型)。
与其他类型类似。
话虽如此,您可以使用您的配方执行此操作,而标准Haskell列表则无法执行此操作:
instance Comonad List
where extract (List a _) = a
duplicate x @ (List _ y) = List x (duplicate y)
可能列表仍然不会是comonadic。
答案 4 :(得分:1)
当我第一次开始使用Haskell时,我也试图尽可能多地用现有类型来表示事物,理由是避免冗余是很好的。我目前的理解(移动目标!)往往涉及更多的多维度权衡网络的想法。我不会在这里给出任何“回答”,就像粘贴例子并问“你明白我的意思吗?”我希望无论如何它都会有所帮助。
让我们看一下Darcs代码:
data UseCache = YesUseCache | NoUseCache
deriving ( Eq )
data DryRun = YesDryRun | NoDryRun
deriving ( Eq )
data Compression = NoCompression
| GzipCompression
deriving ( Eq )
你注意到这三种类型都可以Bool
吗?为什么你认为Darcs黑客决定他们应该在他们的代码中引入这种冗余?另一个例子,这是我们几年前改变的一段代码:
type Slot = Maybe Bool -- OLD code
data Slot = InFirst | InMiddle | InLast -- newer code
为什么你认为我们决定第二个代码比第一个代码有所改进?
最后,这里有一些我日常工作中的代码。它使用了提到的newtype
语法,
newtype Role = Role { fromRole :: Text }
deriving (Eq, Ord)
newtype KmClass = KmClass { fromKmClass :: Text }
deriving (Eq, Ord)
newtype Lemma = Lemma { fromLemma :: Text }
deriving (Eq, Ord)
在这里你会注意到我已经完成了一个好奇的事情,即采用一种非常好的Text
类型然后将它包装成三种不同的东西。与普通的Text
相比,这三件事没有任何新功能。他们只是在那里与众不同。说实话,我不完全确定这对我来说是不是一个好主意。我暂时认为这是因为我出于很多原因操纵了许多不同的文字,但是时间会证明。
你能看到我想要的东西吗?