克隆羊型应如何构建

How should the cloned sheep type be constructed

所以在Haskell monads的解释中他们举了克隆羊的例子。 但是他们只给出了数据类型的部分实现:

type Sheep = ...

father :: Sheep -> Maybe Sheep
father = ...

mother :: Sheep -> Maybe Sheep
mother = ...

这使得试验和理解 comb 等 monad 的用法变得相当困难:

-- comb is a combinator for sequencing operations that return Maybe
comb :: Maybe a -> (a -> Maybe b) -> Maybe b
comb Nothing  _ = Nothing
comb (Just x) f = f x

Sheep应该如何实施?

Sheep 的一个最小实现是

data Sheep = Sheep {mother :: Maybe Sheep, father :: Maybe Sheep}

一个更理智的实现,你可以有兄弟姐妹(可以通过他们的 name 来区分)

data Sheep = Sheep {name :: String, mother :: Maybe Sheep, father :: Maybe Sheep}

这个答案是为了解决您在评论中所做的澄清:

the given definition is of type and the answer is of data, which I don't understand yet how this can happen.


Haskell 有几种不同的类型声明方式。

type关键字用于声明一个类型别名——类似于C++中的typedefusing。理论上,作为徒劳的练习,可以将 Sheep 声明为 String 的别名,隐含的理解是 String 表示羊的名字,然后有一个 "global" 每只羊的 parents 列表,motherfather 函数将用于查找:

type Sheep = String

allParents :: [(Sheep, (Sheep, Sheep))]
allParents =
  [ ("dolly", ("rufus", "peggy"))
  , ("peggy", ("ramses", "woolly"))
  , ...
  ]

mother :: Sheep -> Maybe Sheep
mother s = fst <$> lookup s allParents

father :: Sheep -> Maybe Sheep
father s = snd <$> lookup s allParents

但这 非常 不寻常并且根本不起作用。我认为这将是 gorilla holding a banana and the entire jungle.

的主要示例

一种更 Haskell 的情况建模方法是将 Sheep 声明为具有(至少)两个字段的数据结构 - motherfather。对于这种事情,可以使用关键字 data:

data Sheep = Sheep (Maybe Sheep) (Maybe Sheep)

father :: Sheep -> Maybe Sheep
father (Sheep f _) = f

mother :: Sheep -> Maybe Sheep
mother (Sheep _ m) = m

但是为你的类型的每个字段定义这些访问器函数是很普通的,所以 Haskell 有一个更短的语法:

data Sheep = Sheep { father :: Maybe Sheep, mother :: Maybe Sheep }

这将声明具有两个字段的相同结构,plus 自动创建访问器函数,其工作方式与前面的示例相同。

为了抢占你的下一个问题,我还应该提到第三种定义类型的方法 - newtype,它类似于 data,但仅适用于只有一个字段的结构,并且只是一个性能优化。


不要纠结于所使用的特定关键字。这篇文章只需要指出有这种类型和两个函数,它们是如何定义的并不重要。这篇文章不得不用一些关键词来说明这一点,他们选择了type。或多或少的任意选择。

Sheep 的定义确实无关紧要。 motherfather 的定义 使用 重要的定义。以下将起作用

type Sheep = Int

mother :: Sheep -> Maybe Sheep
mother 0 = Nothing
mother 1 = Nothing
mother n = Just (n `div` 2)

father :: Sheep -> MaybeSheep
father 0 = Nothing
father 1 = Nothing
father n = Just (n `mod` 2)

如愿

type Sheep = String

mother :: Sheep -> Maybe Sheep
mother (x:_:rest) = Just [x]
mother _ = Nothing

father :: Sheep -> Maybe Sheep
father (_:x:rest) = Just [x]
father _ = Nothing

(在这两种情况下,我都不太关心 motherfather 的意思 ;这一点说明了类型才是最重要的。 )

comb本身其实并不关心什么motherfatherreturn;它只是处理试图找到非绵羊的母亲或父亲的问题。只要类型正确,您就可以将 motherfathercomb 一起使用。

comb (Just name) mother == mother name -- name :: Sheep, so mother name :: Maybe Sheep
comb Nothing mother == Nothing -- Nothing :: Maybe Sheep in both cases