在Haskell中使用中间多态类型组合函数



我有以下文件:

module SimpleComposition where
class Intermediate a where
f :: a -> Int
g :: Char -> a
h :: Char -> Int
h = f . g

当尝试在ghci中加载时,我得到错误:

main.hs:8:5: error:
* No instance for (Intermediate a0) arising from a use of `f'
* In the first argument of `(.)', namely `f'
In the expression: f . g
In an equation for `h': h = f . g
|
8 | h = f . g
|     ^

我认为问题是,有人可能在这个组合中使用了两种不同类型的Intermediate实例。我如何保证在导出此模块时它是相同的?

PS:这是一个比我之前问的问题(如何在Haskell中组成多态函数?(更好的问题的最小例子。

问题并不在于无法推断实例,而是编译器确实无法知道您可能想要的类型。g可以生成向其请求的任何类型(前提是它有一个Intermediate实例(,f可以使用任何这样的类型。。。但没有人指定哪一个

但这很容易解决:现在只需选择一种类型。当然,它需要有一个实例;例如,如果你有

instance Intermediate Char where
f = fromEnum
g = id

然后你可以使用

h :: Char -> Int
h = (f :: Char -> Int) . g

修复类型选择的一种更简洁的方法是使用语法扩展:

{-# LANGUAGE TypeApplications #-}
h = f @Char . g

或者,强调你只是在修复中间的类型,

h = f . id @Char . g

我认为问题是有人可能在这个组合中使用了两种不同类型的Intermediate实例。

问题是Haskell不能再从签名中派生出要使用的a。假设有两种Intermediate类型:

instance Intermediate Char where
# …
instance Intermediate Bool where
# …

现在h有两种实现方式:

h :: Char -> Int
h = f . (g :: Char ->Char)

或:

h :: Char -> Int
h = f . (g :: Char -> Bool)

可以使用无限数量的CCD_ 8类型。问题是Haskell无法根据类型签名来判断要使用什么类型。

我们可以给它一个类型提示,但这当然意味着中间类型是固定的。

解决此问题的一个简单方法是使用asTypeOf :: a -> a -> a。这基本上是一个const函数,但其中两个参数具有相同的类型。因此,它用于添加要使用的类型的提示,例如:

h :: Intermediate a =>a-> Char -> Int
h a x = f (g x`asTypeOf`a)

这里的a参数因此不具有任何重要性;注入";将用作CCD_ 12的结果的类型和CCD_。

如果以后使用h,则可以使用:

h(undefined :: Char)'a'

以指定f应具有类型Char -> Charg应具有类型Char -> Int

就像@leftround和@DanielWagner所说的,一个更干净的解决方案,不使用这样的伪变量,就是在签名中添加类型变量:

{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}
h :: forall a. Intermediate a =>Char ->Int
h = f . g @ a

那么我们可以将h与类型变量一起使用:

h@ Char'a'

最新更新