如何匹配签名上的空列表类型包装器类型。针对类型签名函数 returns Nil

How to match empty List type wrapper type on signature. Against type signature the function returns Nil

仍然是关于“PureScript 示例”的第 3 章(不相关 )。函数 removeDuplicates returns me Nil on test 和 repl returns Nil 而不是 AddressBook 这有点令人失望,因为我希望编译器可以防止这种情况。另一方面,我似乎也无法匹配一个空的地址簿(type AddressBook = List Entry)。

代码(省略无关部分):

emptyBook :: AddressBook
emptyBook = empty

findEntry :: String -> String -> AddressBook -> Maybe Entry
findEntry firstName lastName = head <<< filter filterEntry
  where
  filterEntry :: Entry -> Boolean
  filterEntry entry = entry.firstName == firstName && entry.lastName == lastName

-- implementation which fails test/breaks compiler warranty

import Data.AddressBook
import Prelude

import Control.Plus (empty)
import Data.List (List(..), filter, foldl, head, null, tail, (:))


removeDuplicates :: AddressBook -> AddressBook
removeDuplicates book = skipIfDup emptyBook book

skipIfDup :: AddressBook -> AddressBook -> AddressBook
skipIfDup newBook Nil = newBook
skipIfDup newBook (entry : book) =
    skipIfDup newerBook book
    where
    newerBook :: AddressBook
    newerBook = 
        case (findEntry entry.firstName entry.lastName newBook) of
            Nothing -> newBook
            Just e -> insertEntry e newBook

除了 Nil,我还尝试了 Data.List.nullemptyBook(但我相信符号会反弹),结果相同。使用 [] 不编译,将 case 带到主用例的 if-else 语句中,但随后编译器抱怨没有转换 Nil 用例。

这是怎么回事?为什么编译器允许 Nil 返回函数?我应该如何使用 null 或任何必要的符号正确测试空 List/AddressBook 的用例?

REPL 输出:

> removeDuplicates book with duplicate
Nil

测试用例输出:

☠ Failed: Exercise - removeDuplicates because expected ({ address: { city: "Faketown", state: "CA", street: "123 Fake St." }, firstName: "John", lastName: "Smith" } : { address: { city: "Arlen", state: "TX", street: "84 Rainey St." }, firstName: "Peggy", lastName: "Hill" } : { address: { city: "Springfield", state: "USA", street: "740 Evergreen Terrace" }, firstName: "Ned", lastName: "Flanders" } : Nil), got Nil

与 C# 或 JavaScript 或您来自的任何地方不同,PureScript 中的 Nilnull 不是 special/magic“未初始化引用”类型的事情。 PureScript 根本没有这些。就 PureScript 编译器所知,一切总是“定义的”。

Nil 只是 List 构造函数的名称。看看 the List definition:

data List a = Nil | Cons a (List a)

它只是一个 sum 类型,有两个构造函数,其中一个名为 Nil。可以命名为 Abracadabra 并且仍然可以正常工作,但显然库作者希望名称具有某种意义,我猜?愚蠢的人。所以 Nil 在这种情况下应该表示“空列表”。

同样,null只是一个函数名。 Here's its definition:

null :: forall a. List a -> Boolean
null Nil = true
null _ = false

只是 returns true 是给定的列表是一个空列表,否则 false


现在,您的实际问题出在这里:

    newerBook = 
        case (findEntry entry.firstName entry.lastName newBook) of
            Nothing -> newBook
            Just e -> insertEntry e newBook

看看你在做什么:你试图在 newBook 中找到条目,如果找到,你将相同的条目插入到 newBook 中。这样 newBook 现在就有两个这样的条目。这真的是你想要做的吗?

根据我对原始问题的模糊理解,你真正想要做的是将 entry 添加到 newBook 如果它是 而不是 已经。如果 在那里,请单独留下 newBook

    newerBook = 
        case (findEntry entry.firstName entry.lastName newBook) of
            Nothing -> entry : newBook
            Just _ -> newBook