简体   繁体   English

在 Haskell 中组合具有中间多态类型的函数

[英]Composing functions with a intermediate polymorphic type in Haskell

I have the following file:我有以下文件:

module SimpleComposition where

class Intermediate a where
    f :: a -> Int
    g :: Char -> a

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

When trying loading it in ghci, I get the error:尝试在 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
  |     ^

I believe the problem is that someone could be using 2 different types which are instances of Intermediate in this composition.我认为问题在于有人可能会使用 2 种不同类型,它们是此组合中的Intermediate实例。 How can I guarantee it is the same when I export this module?当我导出这个模块时,我如何保证它是一样的?

PS: This is a better minimal example of the problem I have than the question I asked before ( How to compose polymorphic functions in Haskell? ). PS:这是一个比我之前提出的问题更好的最小示例(如何在 Haskell 中组合多态函数? )。

The problem isn't really that the instance can't be inferred, but that the compiler really has no way of knowing what type you could possibly want there.问题不在于无法推断出实例,而是编译器确实无法知道您可能想要的类型。 g can produce any type that's asked from it (provided it has an Intermediate instance), f can consume any such type... but nobody specifies which one . g可以产生任何向它询问的类型(前提是它有一个Intermediate实例), f可以使用任何这样的类型......但没有人指定哪一个

But that's easy to fix: just select a type now.但这很容易解决:现在只需 select 一个类型。 Of course it needs to be one that does have an instance;当然,它必须是有实例的; eg if you have例如,如果你有

instance Intermediate Char where
  f = fromEnum
  g = id

then you can use那么你可以使用

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

A more concise way of fixing the type choice is to use a syntactic extension:修复类型选择的更简洁的方法是使用语法扩展:

{-# LANGUAGE TypeApplications #-}

h = f @Char . g

...or, to emphasize that you're just fixing the type in the middle, ...或者,为了强调您只是在中间固定类型,

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.我认为问题在于有人可能会使用 2 种不同类型,它们是此组合中的中间实例。

No the problem is that Haskell can no longer derive from the signature what a to use.没有问题是 Haskell 不能再从签名派生什么a要使用。 Imagine that there are two Intermediate types:假设有两种Intermediate类型:

instance Intermediate Char where
    # …

instance Intermediate Bool where
    # …

now there are two implementations for h :现在h有两种实现:

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

or:或者:

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

there can be an infinite number of Intermediate types that can be used.可以使用无数种Intermediate类型。 The problem is that Haskell can not tell, based on the type signature what type to use.问题是 Haskell 无法根据类型签名判断使用什么类型。

We can give it a type hint, but that of course means that the intermediate type is fixed.我们可以给它一个类型提示,但这当然意味着中间类型是固定的。

A simple way to fix this is making use of asTypeOf:: a -> a -> a .解决此问题的一种简单方法是使用asTypeOf:: a -> a -> a This is basically a const function, but where the two parameters have the same type.这基本上是一个const function,但是两个参数具有相同的类型。 This is thus used to add a hint what type to use, for example:因此,这用于添加提示要使用什么类型,例如:

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

Here the value a parameter is thus not of any importance, this is a way to "inject" the type that will be used as type for the result of g and the parameter of f .因此,此处参数a并不重要,这是一种“注入”类型的方法,该类型将用作g的结果和f的参数的类型。

If you thus later use h , you can work with:如果您以后使用h ,您可以使用:

h (undefined :: Char) 'a'

to specify that f should have type Char -> Char , and g should have type Char -> Int .指定f应该具有类型Char -> Char ,并且g应该具有类型Char -> Int

Like @leftroundabout and @DanielWagner say, a more clean solution without making use of such dummy variable , is adding the type variable in the signature:就像@leftroundabout@DanielWagner说的那样,不使用这种虚拟变量的更干净的解决方案是在签名中添加类型变量:

{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}

h :: forall a. Intermediate a => Char -> Int
h = f . g @ a

then we can use h with a type variable with:然后我们可以将h与类型变量一起使用:

h @ Char 'a'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM