在 Haskell 中使用中间多态类型组合函数
Composing functions with a intermediate polymorphic type in 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
| ^
我认为问题在于有人可能使用了 2 种不同的类型,它们是此组合中 Intermediate
的实例。我导出这个模块怎么保证是一样的?
PS:与我之前提出的问题 (How to compose polymorphic functions in Haskell?) 相比,这是我遇到的问题的一个更好的最小示例。
问题不在于无法推断出实例,而是编译器确实无法知道您可能需要什么类型。 g
可以生成任何类型(前提是它有一个 Intermediate
实例),f
可以使用任何此类类型...但没有人指定 哪个.
但这很容易修复:现在只需 select 一个类型。当然,它需要是一个确实有实例的;例如如果你有
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
I believe the problem is that someone could be using 2 different types which are instances of Intermediate in this composition.
不,问题是 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)
可以使用无限多种 Intermediate
类型。问题是 Haskell 无法根据类型签名判断使用什么类型。
我们可以给它一个类型提示,但这当然意味着中间类型是固定的。
解决这个问题的一个简单方法是使用 asTypeOf :: a -> a -> a
。这基本上是一个 const
函数,但是两个参数具有相同的类型。因此,这用于添加要使用的类型的提示,例如:
h :: Intermediate a => <b>a</b> -> Char -> Int
h a x = f (g x <b>`asTypeOf`</b> a)
这里的 value a
参数并不重要,这是一种“注入”将用作结果类型的类型的方法g
的参数和 f
.
的参数
如果您以后使用 h
,您可以使用:
h <b>(undefined :: Char)</b> 'a'
指定 f
应具有类型 Char -> Char
,并且 g
应具有类型 Char -> Int
.
就像 and 说的那样,不使用这种虚拟变量的更干净的解决方案是在签名中添加类型变量:
{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}
h :: forall a. Intermediate a => Char -> Int
h = f . g @ a
然后我们可以将 h
与类型变量一起使用:
h <b>@ Char</b> 'a'
我有以下文件:
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
| ^
我认为问题在于有人可能使用了 2 种不同的类型,它们是此组合中 Intermediate
的实例。我导出这个模块怎么保证是一样的?
PS:与我之前提出的问题 (How to compose polymorphic functions in Haskell?) 相比,这是我遇到的问题的一个更好的最小示例。
问题不在于无法推断出实例,而是编译器确实无法知道您可能需要什么类型。 g
可以生成任何类型(前提是它有一个 Intermediate
实例),f
可以使用任何此类类型...但没有人指定 哪个.
但这很容易修复:现在只需 select 一个类型。当然,它需要是一个确实有实例的;例如如果你有
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
I believe the problem is that someone could be using 2 different types which are instances of Intermediate in this composition.
不,问题是 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)
可以使用无限多种 Intermediate
类型。问题是 Haskell 无法根据类型签名判断使用什么类型。
我们可以给它一个类型提示,但这当然意味着中间类型是固定的。
解决这个问题的一个简单方法是使用 asTypeOf :: a -> a -> a
。这基本上是一个 const
函数,但是两个参数具有相同的类型。因此,这用于添加要使用的类型的提示,例如:
h :: Intermediate a => <b>a</b> -> Char -> Int
h a x = f (g x <b>`asTypeOf`</b> a)
这里的 value a
参数并不重要,这是一种“注入”将用作结果类型的类型的方法g
的参数和 f
.
如果您以后使用 h
,您可以使用:
h <b>(undefined :: Char)</b> 'a'
指定 f
应具有类型 Char -> Char
,并且 g
应具有类型 Char -> Int
.
就像
{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}
h :: forall a. Intermediate a => Char -> Int
h = f . g @ a
然后我们可以将 h
与类型变量一起使用:
h <b>@ Char</b> 'a'