我是伊德里斯的新手。我正在试验类型,我的任务是制作一个"洋葱":一个接受两个参数的函数:一个数字和任何参数,并将任何内容放入嵌套List
这样的次数。
例如,mkOnion 3 "Hello World"
的结果应[[["Hello World"]]]
。我做了这样一个函数,这是我的代码:
onionListType : Nat -> Type -> Type
onionListType Z b = b
onionListType (S a) b = onionListType a (List b)
mkOnionList : (x : Nat) -> y -> onionListType x y
mkOnionList Z a = a
mkOnionList (S n) a = mkOnionList n [a]
prn : (Show a) => a -> IO ();
prn a = putStrLn $ show a;
main : IO()
main = do
prn $ mkOnionList 3 4
prn $ mkOnionList 2 'a'
prn $ mkOnionList 5 "Hello"
prn $ mkOnionList 0 3.14
程序工作的结果:
[[[4]]] [['a']] [[[[["Hello"]]]]] 3.14
这正是我所需要的。但是当我做同样的事情时,但像这样将 Nat 更改为整数
onionListTypeI : Integer -> Type -> Type
onionListTypeI 0 b = b
onionListTypeI a b = onionListTypeI (a-1) (List b)
mkOnionListI : (x : Integer) -> y -> onionListTypeI x y
mkOnionListI 0 a = a
mkOnionListI n a = mkOnionListI (n-1) [a]
我收到一个错误:
When checking right hand side of mkOnionListI with expected type onionListTypeI 0 y Type mismatch between y (Type of a) and onionListTypeI 0 y (Expected type)
为什么类型检查失败?
我认为这是因为Integer
可以取负值,而在负值的情况下无法计算Type
。如果我是对的,编译器如何理解这一点?
你是对的,无法计算类型。但那是因为onionListTypeI
不是完全的。您可以在 REPL 中检查这一点
*test> :total onionListTypeI
Main.onionListTypeI is possibly not total due to recursive path:
Main.onionListTypeI, Main.onionListTypeI
(甚至更好,要求源代码中的%default total
,这会引发错误。
由于类型构造函数不是完全的,因此编译器不会将onionListTypeI 0 y
规范化为 y
。由于案例onionListTypeI a b = onionListTypeI (a-1) (List b)
,它不是全部的。编译器只知道从Integer
中减去 1 会得到一个Integer
,但不知道确切的数字(不像用 Nat
做)。这是因为Integer
、Int
、Double
和各种Bits
的算术都是用prim__subBigInt
这样的主要函数定义的。如果这些函数不是盲目的,编译器应该有负值的问题,就像你假设的那样。