在 Haskell 中查找组合函数的类型
Finding the type of a composition function in Haskell
假设我在 Haskell 中有以下功能:
compose2 = (.) . (.)
我将如何找到这个函数的类型?由于多种组合,我无法确定此函数的类型。
我知道我可以使用 :t compose2
来获取类型。但是,我想知道如何在没有计算机帮助的情况下做到这一点。我应该采取什么步骤来找到类型?
如果我们先给组合函数一个更独特的标识符可能会有所帮助,例如:
compose2 = (.)<sub>2</sub> .<sub>1</sub> (.)<sub>3</sub>
这样更容易引用某些函数。我们还可以将其转换为更规范的形式,例如:
compose2 = ((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub>
所以现在我们可以开始推导函数类型了。我们知道 (.)
有类型 (.) :: (b -> c) -> (a -> b) -> a -> c
,或者更规范的 (.) :: (b -> c) -> ((a -> b) -> (a -> c))
。由于类型变量不是“全局”的,因此我们可以为树函数赋予类型变量不同的名称:
(.)<sub>1</sub> :: (b -> c) -> ((a -> b) -> (a -> c))
(.)<sub>2</sub> :: (e -> f) -> ((d -> e) -> (d -> f))
(.)<sub>3</sub> :: (h -> i) -> ((g -> h) -> (g -> i))
现在我们已经为不同的组合函数赋予了签名,我们可以开始推导类型了。
我们知道(.)<sub>2</sub>
是用(.)[=函数应用的参数68=]1</sub>
,所以这意味着参数的类型 (b -> c)
与类型 (e -> f) -> ((d -> e) -> (d -> f))
相同,因此 b ~ (e -> f)
,和 c ~ ((d -> e) -> (d -> f))
.
我们进一步知道 (.)<sub>1</sub>
的“第二个”参数的类型与 (.)<sub>3</sub>
,因此 (a -> b) ~ ((h -> i) -> ((g -> h) -> (g -> i)))
,因此 a ~ (h -> i)
,以及 b ~ ((g -> h) -> (g -> i))
,因此“return type" of (.)<sub>1</sub>
,即 (a -> c)
因此可以特化为:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: a -> c
并且由于 a ~ (h -> i)
,并且 c ~ (d -> e) -> (d -> f)
:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: (h -> i) -> ((d -> > e) -> (d > f))
我们知道 b
等价于 b ~ (e -> f)
和 b ~ ((g -> h) -> (g -> i))
,所以这意味着 e ~ (g -> h)
和 f ~ (g -> i)
,因此我们可以专门化签名进一步到:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: (h -> i) -> ((d -> (g -> h)) -> (d -> (g -> i)))
这是更详细的形式:
(.)<sub>2</sub> .<sub>1</sub> (.)<sub>3</sub> :: (h -> i) -> (d -> g -> h) -> d -> g -> i
如果我们自动推导类型,我们得到:
Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a1 -> a -> b) -> a1 -> a -> c
如果我们因此将 b
替换为 h
,将 c
替换为 i
,将 a1
替换为 d
和 a
使用g
,我们得到相同的类型。
假设我在 Haskell 中有以下功能:
compose2 = (.) . (.)
我将如何找到这个函数的类型?由于多种组合,我无法确定此函数的类型。
我知道我可以使用 :t compose2
来获取类型。但是,我想知道如何在没有计算机帮助的情况下做到这一点。我应该采取什么步骤来找到类型?
如果我们先给组合函数一个更独特的标识符可能会有所帮助,例如:
compose2 = (.)<sub>2</sub> .<sub>1</sub> (.)<sub>3</sub>
这样更容易引用某些函数。我们还可以将其转换为更规范的形式,例如:
compose2 = ((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub>
所以现在我们可以开始推导函数类型了。我们知道 (.)
有类型 (.) :: (b -> c) -> (a -> b) -> a -> c
,或者更规范的 (.) :: (b -> c) -> ((a -> b) -> (a -> c))
。由于类型变量不是“全局”的,因此我们可以为树函数赋予类型变量不同的名称:
(.)<sub>1</sub> :: (b -> c) -> ((a -> b) -> (a -> c))
(.)<sub>2</sub> :: (e -> f) -> ((d -> e) -> (d -> f))
(.)<sub>3</sub> :: (h -> i) -> ((g -> h) -> (g -> i))
现在我们已经为不同的组合函数赋予了签名,我们可以开始推导类型了。
我们知道(.)<sub>2</sub>
是用(.)[=函数应用的参数68=]1</sub>
,所以这意味着参数的类型 (b -> c)
与类型 (e -> f) -> ((d -> e) -> (d -> f))
相同,因此 b ~ (e -> f)
,和 c ~ ((d -> e) -> (d -> f))
.
我们进一步知道 (.)<sub>1</sub>
的“第二个”参数的类型与 (.)<sub>3</sub>
,因此 (a -> b) ~ ((h -> i) -> ((g -> h) -> (g -> i)))
,因此 a ~ (h -> i)
,以及 b ~ ((g -> h) -> (g -> i))
,因此“return type" of (.)<sub>1</sub>
,即 (a -> c)
因此可以特化为:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: a -> c
并且由于 a ~ (h -> i)
,并且 c ~ (d -> e) -> (d -> f)
:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: (h -> i) -> ((d -> > e) -> (d > f))
我们知道 b
等价于 b ~ (e -> f)
和 b ~ ((g -> h) -> (g -> i))
,所以这意味着 e ~ (g -> h)
和 f ~ (g -> i)
,因此我们可以专门化签名进一步到:
((.)<sub>1</sub> (.)<sub>2</sub>) (.)<sub>3</sub> :: (h -> i) -> ((d -> (g -> h)) -> (d -> (g -> i)))
这是更详细的形式:
(.)<sub>2</sub> .<sub>1</sub> (.)<sub>3</sub> :: (h -> i) -> (d -> g -> h) -> d -> g -> i
如果我们自动推导类型,我们得到:
Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a1 -> a -> b) -> a1 -> a -> c
如果我们因此将 b
替换为 h
,将 c
替换为 i
,将 a1
替换为 d
和 a
使用g
,我们得到相同的类型。