Haskell 多ramTypeClass的类型推断?

Haskell 多ramTypeClass的类型推断?,haskell,Haskell,我写了一些我认为显然是正确的代码,但GHC似乎不这么认为: class Convert a b where convert :: a -> b class (Convert a b, Convert b c) => F a b c where f :: a -> c f = f2 . f1 where f2 = convert :: b -> c f1 = convert :: a -> b 上面的

我写了一些我认为显然是正确的代码,但GHC似乎不这么认为:

class Convert a b where
    convert :: a -> b
class (Convert a b, Convert b c) => F a b c where 
    f  :: a -> c
    f = f2 . f1 
      where f2 = convert :: b -> c
            f1 = convert :: a -> b
上面的代码给出了这样的错误信息,所以我想知道GHC在尝试推断正确的类型时遇到了什么困难,或者我必须提供GHC更多的类型信息吗

Main.hs:53:18:
    Could not deduce (Convert b2 c2) ac)
      bound by the class declaration for ‘F’ at Main.hs:(50,1)-(54,34)
    Possible fix:
      add (Convert b2 c2) to the context of
        an expression type signature: b2 -> c2
    In the expression: convert :: b -> c
    In an equation for ‘f2’: f2 = convert :: b ->    where
              f2 = convert :: b -> c
              f1 = convert :: a -> b

Main.hs:54:18:
    Could not deduce (Convert a2 b2) arising from a use of ‘convert’
    from the context (F a b c)
      bound by  Possible fix:
      add (Convert a2 b2) to the context of
        an expression type signature: a2 -> b2
    In the expression: convert :: a -> b
    In an equation for ‘f1’: f1 = convert :: a -> b
    In an equation for ‘f’:
        f = f2 . f1
          where
              f2 = convert :: b -> c
              f1 = convert :: a -> b
Failed, modules loaded: none.
f1和f2都使用b,但这是自由的,不受f的约束。ghci如何确定b

可以使用ScopedTypeVariables将f1和f2上的b类型与类上的b约束链接起来

{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
class Convert a b where
    convert :: a →  b
class (Convert a b, Convert b c) ⇒  F a b c | a c → b where -- #1
    f  :: a →  c
    f = f2 . f1
      where f2 = convert :: ∀ b . F a b c ⇒ b →  c -- #2
            f1 = convert :: ∀ b . F a b c ⇒ a →  b -- #3

instance Convert Int String where
  convert = show
instance Convert String Double where
  convert = read
instance F Int String Double where
现在

没有ScopedTypeVariables,1、2和3上的b类型名称都是不同的,尽管f2推断出2和3是相同的。f1。对于ScopedTypeVariables,所有1、2和3都是相同的类型

另一方面,从两个给定的a和c中选择b需要函数依赖性,因为f没有关于它的信息

最后,看看@leftaroundabout response,如果可以的话,最好使用phantom类型指定f上的b类型约束

相关问题

f的签名不包含任何b。因此,没有办法指定b应该是什么!但是你需要这些信息;对于一个极端的例子,考虑转换双->int>双,这显然需要将结果与微不足道的转换双->双->双,这是不会改变任何东西的。有两种方法可以解决这个问题:

使b在功能上依赖于a或c,这些可以从签名中读取。您可以在Convert中正确执行此操作

…但是,这意味着每种类型只能转换为另一种类型!对于任何a,您只能指定一个实例转换为b,否则它不是函数依赖关系。-或者,你可以用F来做

是的,这是一件非常烦人的事情:类中的类型变量Convert a b,Convert b c=>fa b c没有作用域。您可以在ScopedTypeVariables扩展的作用域中显式地引入b类型变量,但这需要显式的通用限定签名。←这在这里是不必要的,见josejuan的答案。这种方法更有意义,因为您仍然可以进行多路转换

在调用站点显式指定b。如果给f一个带标签的类型签名,则可以执行此操作:

import Data.Tagged

class Convert a b where
  convert :: a -> b
class (Convert a b, Convert b c) => F a b c where 
  f  :: Tagged b (a -> c)
  f = f'
   where f' :: ∀ a b c . F a b c => Tagged b (a -> c)
         f' = Tagged (f2 . f1)
          where f1 = convert :: a -> b
                f2 = convert :: b -> c
与f上的fundep相比,它的优点是现在可以指定任何您喜欢的fabc实例。使用fundep,对于任何a和c,您只能选择一个b。另一方面,现在调用f-see更麻烦了,尽管如此


†如果您实际上不需要添加Convert Double Int实例,那么我建议您不要这样做。

有趣!为什么不在这里显式地将b引入f的作用域?@leftaroundabout FlexibleInstances选择中间实例字符串在这种情况下为什么这里需要forall?我试着在你的源代码中删除它,但它仍然有效。我以前从未使用过forall…通常,当您使用ScopedTypeVariables编写函数时,使用forall将作用域类型变量设置为全身可见。在本例中,主体只是一个表达式。
{-# LANGUAGE FunctionalDependencies #-}

class Convert a b | a -> b where
  convert :: a -> b
class (Convert a b, Convert b c) => F a b c where 
  f  :: a -> c
  f = convert . convert
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}

class Convert a b where
  convert :: a -> b
class (Convert a b, Convert b c) => F a b c | a c -> b where 
  f  :: a -> c
  f = f'
   where f' :: ∀ a b c . F a b c => a -> c
         f' = f2 . f1
          where f1 = convert :: a -> b
                f2 = convert :: b -> c
import Data.Tagged

class Convert a b where
  convert :: a -> b
class (Convert a b, Convert b c) => F a b c where 
  f  :: Tagged b (a -> c)
  f = f'
   where f' :: ∀ a b c . F a b c => Tagged b (a -> c)
         f' = Tagged (f2 . f1)
          where f1 = convert :: a -> b
                f2 = convert :: b -> c