如何手动确定以下功能的最常用类型?

时间:2012-02-29 13:46:03

标签: haskell types lambda currying

目前我正在学习Haskell。我们必须确定给定函数的最一般类型,但我还没有得到它。解释器如何确定函数的最一般类型,尤其是lambda表达式?什么是手动确定最常规类型的安全方法?

tx2 = (\x y z -> y.z.z)

tx2::a->(a->b)->(a->a)->b -- my guess

tx2 :: a -> (b -> c) -> (b -> b) -> b -> c -- interpreter solution

如果第一个变量(a)应用于表达式z,则z必须将a作为输入参数,但它在(b-> b)中使用b。 y消耗b并生成c,因此最终结果必须为c。但为什么b(作为中间结果?)包含在类型中?如果是这样,为什么它不是 - > (b - > c) - > (b - > b) - > B-> b - > c?

tm2 = (\i -> [sum,product]!!i)

tm2:: Int->[(Integer->Integer->Integer)]->(Integer->Integer->Integer) -- my guess

\i -> [sum,product] !! i :: Num a => Int -> [a] -> a -- interpreter with direct input
tm2 :: Int -> [Integer] -> Integer -- interpreter with :info tm2

因此,如果tm2在脚本中编码,解释器有关于类型的更详细信息,对吧?所以第二行中的类型是表达式的结果。为什么第2行只接受整数,而不是Float例如?

tp2 = (\x -> \y -> (x.y.x))

tp2::(a->b)->((a->b)->a)->a -- my guess

tp2 :: (a -> b) -> (b -> a) -> a -> b -- interpreter solution

为什么我必须在类型中包含中间结果?为什么没有使用(a-> b) - > a表示下面的tf2?

tf2 = (\x -> \y -> (x (y x), x, y))

tf2::(a->b)->((a->b)->a)->(a,a->b,(a->b)->a) -- solution


tg2 = (\x y z a -> y(z(z(a))));

tg2::a->(b->c)->(b->b)->b->c -- solution

这里我们不需要任何中间结果?我们写下参数的类型,然后写下结果的类型?

2 个答案:

答案 0 :(得分:8)

tx2 = (\x y z -> y.z.z)

tx2有三个参数(为了便于讨论,我忽略了currying),忽略了第一个并且组成第二个和第三个两次。所以第一个参数可以有任何类型,第二个和第三个必须有函数类型,比如说

y :: ay -> ry
z :: az -> rz

现在,z的第一个应用程序的结果成为z的第二个应用程序的参数,因此zrz的结果类型必须是参数类型为zaz,我们称之为b,所以

z :: b -> b

然后z应用程序的结果成为y的参数,因此y的参数类型必须与z的结果类型相同,但y的结果类型完全不受表达式约束,因此

y :: b -> c

y . z . z :: b -> c,因此

tx2 :: a -> (b -> c) -> (b -> b) -> (b -> c)

然后是下一个,

tm2 = (\i -> [sum,product]!!i)

现在,sumproduct是来自Prelude的函数,两者的类型都为Num a => [a] -> a,因此

Prelude> :t [sum,product]
[sum,product] :: Num a => [[a] -> a]

(!!) :: [e] -> Int -> e以来,给定xs类型的列表[e],表达式\i -> xs !! i的类型为Int -> e。因此推断的tm2 = \i -> [sum,product] !! i类型是

tm2 :: Num a => Int -> ([a] -> a)

但是,tm2受到没有类型签名的简单模式绑定的约束,因此monomorphism restriction启动,tm2的类型必须是单态的。通过默认规则,通过使用Num a实例化类型变量a来解决Integer约束(除非明确的默认声明另有说明),所以你得到

Prelude> let tm2 = \i -> [sum,product] !! i
Prelude> :t tm2
tm2 :: Int -> [Integer] -> Integer

除非您禁用单态限制(:set -XNoMonomorphismRestriction)。

tp2 = (\x -> \y -> (x.y.x))

第一次应用x的结果成为y的参数,因此x的结果类型必须与y的参数类型相同。然后y的应用结果成为x的第二个应用的参数,因此y的结果类型必须是'x`的参数类型,总共

tp2 :: (a -> b) -> (b -> a) -> (a -> b)

然后在

tf2 = (\x -> \y -> (x (y x), x, y))

唯一有趣的部分是结果的第一个组成部分x (y x),因此x应用于y的应用结果,因此x必须具有功能类型

x :: a -> b

y必须包含结果类型a。但是y已应用于x,因此其参数类型必须是x的类型,

y :: (a -> b) -> a

tf2 :: (a -> b) -> ((a -> b) -> a) -> (b, a -> b, (a -> b) -> a)

最后

tg2 = (\x y z a -> y(z(z(a))))
顺便说一句,它与tx2完全相同,只是通过在lambda中再提供一个参数来进行扩展。因此,类型的推导也是一样的。

答案 1 :(得分:1)

我认为你对Haskell中函数类型的许多怀疑会在wikipedia上阅读一些关于“currying”的内容得到答案。

你编写的函数的推断类型就是这样,因为Haskell使用curried函数,即只有一个输入参数的函数可以返回(取决于定义)其他函数。 因此,当您编写类似mySum x y = x + y的函数时,其类型为a -> a -> a,通过明确->a -> (a -> a)的正确关联性,可以更轻松地阅读。看到?您获得的额外返回类型不是您调用它们的“中间结果”。 您不需要两个参数并返回结果a。您使用一个参数x,在表达式x + y中修复其值,然后返回带参数的函数a -> a(此次为y)并将其相加到y您之前已修复过。

逐点回答你的问题会有点长,所以我会通过,但这是为了检查你的功能类型你必须做的理由。

对于tm2上的第二个问题,我不确定解释器推断类型之间的区别。