在 Haskell 中,如何避免类型类泛型函数中的模糊类型错误?
How do I avoid ambiguous type errors in generic functions on typeclasses, in Haskell?
我正在尝试为类型类编写通用函数:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Foo1 where
import Data.Foldable
class Foo t where
type FooPType t :: * -- Base type.
type FooFType t :: * -> * -- Container type.
defPs :: FooFType t (FooPType t) -- Initialized container.
-- An attempt at a universal testing function valid for all types, t,
-- of class Foo for which `FooFType t` is a foldable functor.
tst :: forall t.
( Foo t
, Functor (FooFType t)
, Foldable (FooFType t)
) => FooPType t
tst = (head . toList) defPs
但是,我从 GHC (8.0.2) 收到此错误:
Foo1.hs:30:23: error:
• Couldn't match type ‘FooPType t0’ with ‘FooPType t’
Expected type: FooFType t0 (FooPType t)
Actual type: FooFType t0 (FooPType t0)
NB: ‘FooPType’ is a type function, and may not be injective
The type variable ‘t0’ is ambiguous
• In the first argument of ‘head . toList’, namely ‘defPs’
In the expression: (head . toList) defPs
In an equation for ‘tst’: tst = (head . toList) defPs
• Relevant bindings include
tst :: FooPType t (bound at Foo1.hs:30:1)
四处寻找,我发现有些人通过将 "type" 更改为 "data" 来解决这个问题,但这对我不起作用。
(我为 FooFType 更改了它。我应该为 FooPType 更改它吗?两者都更改?)
哦!在发布之前,我应该尝试自己回答最后一个问题。
果然改了这行代码:
type FooPType t :: * -- Base type.
阅读:
data FooPType t :: * -- Base type.
摆脱了我的编译错误。
谁能解释为什么这个改变有效?
这是解决方案,根据@HTNW 的提示,回复:在 "defPs" 之后添加“@t”:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Foo1 where
import Data.Foldable
class Foo t where
type FooPType t :: * -- Base type.
type FooFType t :: * -> * -- Container type.
defPs :: FooFType t (FooPType t) -- Initialized container.
-- An attempt at a universal testing function valid for all types of class Foo.
tst :: forall t.
( Foo t
, Functor (FooFType t)
, Foldable (FooFType t)
) => FooPType t
tst = (head . toList) $ defPs @t
以上代码在GHC 8.0.2下编译w/o错误:
Davids-Air-2:so_noninjective_type_funcs dbanas$ stack ghc -- -c Foo1.hs
Davids-Air-2:so_noninjective_type_funcs dbanas$
tst :: forall t. _ => FooPType t
tst = head $ toList _ -- want f (FooPType t)
defPs :: FooFType u (FooPType u)
tst = head $ toList defPs
-- try to unify (f (FooPType t)) with
-- (FooFType u (FooPType u))
-- Assume that f is injective: f x ~ g y iff f ~ g and x ~ y
-- GHC assumes this because it doesn't allow you to abstract over non-injective
-- type constructors anyway.
-- try to unify f with FooFType u; OK
-- try to unify FooPType t with FooPType u; oops
如果FooPType
是data family
,那么FooPType x ~ FooPType y
表示x ~ y
,因为data family
是单射的。在这里,它只是一个 type family
,这意味着编译器无法推断出您要为类型 t
调用 defPs
。例如,您可以将 FooPType u ~ FooPType t
添加到 test
的上下文中,现在 u
和 t
都是 defPs
.[=26 的有效类型参数=]
test :: forall t u.
( Foo t, Foo u
, Foldable (FooFType t), Foldable (FooFType u)
, FooPType t ~ FooPType u
) => FooPType u
test = head $ toList defPs -- uh oh; which one?
instance Foo Bool where
type FooPType Bool = Int
type FooFType Bool = []
defPs = [1]
instance Foo Int where
type FooPType Int = Int
type FooFType Int = []
defPs = [3]
test @Bool @Int -- 1 or 3?
有趣的是,即使是类型签名也救不了你。似乎有必要使用类型应用程序:
{-# LANGUAGE ExplicitForAll, ScopedTypeVariables, TypeApplications, ... #-}
test :: forall t. (Foo t, Foldable (FooFType t)) => FooPType t
test = head $ toList $ defPs @t
我正在尝试为类型类编写通用函数:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Foo1 where
import Data.Foldable
class Foo t where
type FooPType t :: * -- Base type.
type FooFType t :: * -> * -- Container type.
defPs :: FooFType t (FooPType t) -- Initialized container.
-- An attempt at a universal testing function valid for all types, t,
-- of class Foo for which `FooFType t` is a foldable functor.
tst :: forall t.
( Foo t
, Functor (FooFType t)
, Foldable (FooFType t)
) => FooPType t
tst = (head . toList) defPs
但是,我从 GHC (8.0.2) 收到此错误:
Foo1.hs:30:23: error:
• Couldn't match type ‘FooPType t0’ with ‘FooPType t’
Expected type: FooFType t0 (FooPType t)
Actual type: FooFType t0 (FooPType t0)
NB: ‘FooPType’ is a type function, and may not be injective
The type variable ‘t0’ is ambiguous
• In the first argument of ‘head . toList’, namely ‘defPs’
In the expression: (head . toList) defPs
In an equation for ‘tst’: tst = (head . toList) defPs
• Relevant bindings include
tst :: FooPType t (bound at Foo1.hs:30:1)
四处寻找,我发现有些人通过将 "type" 更改为 "data" 来解决这个问题,但这对我不起作用。 (我为 FooFType 更改了它。我应该为 FooPType 更改它吗?两者都更改?)
哦!在发布之前,我应该尝试自己回答最后一个问题。 果然改了这行代码:
type FooPType t :: * -- Base type.
阅读:
data FooPType t :: * -- Base type.
摆脱了我的编译错误。
谁能解释为什么这个改变有效?
这是解决方案,根据@HTNW 的提示,回复:在 "defPs" 之后添加“@t”:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Foo1 where
import Data.Foldable
class Foo t where
type FooPType t :: * -- Base type.
type FooFType t :: * -> * -- Container type.
defPs :: FooFType t (FooPType t) -- Initialized container.
-- An attempt at a universal testing function valid for all types of class Foo.
tst :: forall t.
( Foo t
, Functor (FooFType t)
, Foldable (FooFType t)
) => FooPType t
tst = (head . toList) $ defPs @t
以上代码在GHC 8.0.2下编译w/o错误:
Davids-Air-2:so_noninjective_type_funcs dbanas$ stack ghc -- -c Foo1.hs
Davids-Air-2:so_noninjective_type_funcs dbanas$
tst :: forall t. _ => FooPType t
tst = head $ toList _ -- want f (FooPType t)
defPs :: FooFType u (FooPType u)
tst = head $ toList defPs
-- try to unify (f (FooPType t)) with
-- (FooFType u (FooPType u))
-- Assume that f is injective: f x ~ g y iff f ~ g and x ~ y
-- GHC assumes this because it doesn't allow you to abstract over non-injective
-- type constructors anyway.
-- try to unify f with FooFType u; OK
-- try to unify FooPType t with FooPType u; oops
如果FooPType
是data family
,那么FooPType x ~ FooPType y
表示x ~ y
,因为data family
是单射的。在这里,它只是一个 type family
,这意味着编译器无法推断出您要为类型 t
调用 defPs
。例如,您可以将 FooPType u ~ FooPType t
添加到 test
的上下文中,现在 u
和 t
都是 defPs
.[=26 的有效类型参数=]
test :: forall t u.
( Foo t, Foo u
, Foldable (FooFType t), Foldable (FooFType u)
, FooPType t ~ FooPType u
) => FooPType u
test = head $ toList defPs -- uh oh; which one?
instance Foo Bool where
type FooPType Bool = Int
type FooFType Bool = []
defPs = [1]
instance Foo Int where
type FooPType Int = Int
type FooFType Int = []
defPs = [3]
test @Bool @Int -- 1 or 3?
有趣的是,即使是类型签名也救不了你。似乎有必要使用类型应用程序:
{-# LANGUAGE ExplicitForAll, ScopedTypeVariables, TypeApplications, ... #-}
test :: forall t. (Foo t, Foldable (FooFType t)) => FooPType t
test = head $ toList $ defPs @t