好的,所以在进行更改之前,我得到了以下简化的工作示例:
data D = D
data C = C
class T a where
t :: a
instance T D where
t = D
instance T C where
t = C
g :: T a => IO a
g = do
return t
main = (g :: IO D) >> return ()
所以问题是,在g
内,我希望根据a
选择不相关的a
类型的值。换句话说,我想表达的是,如果a
是C
,那么将选择尚未提及的类型e
的某个值,如果不是,那么将选择另一个类型e
的值将被选择。它基本上以任意类型相等为条件,例如伪代码if a ~ Bool then "foo" else "bar"
。我这样尝试过(在此示例中,将String
用于类型e
)
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
import Data.Proxy
class F sub1 sub2 where
f :: Proxy (sub1, sub2) -> String
instance {-# OVERLAPPABLE #-} F a b where
f _ = "did not match types"
instance {-# OVERLAPPING #-} F a a where
f _ = "matched types"
data D = D
data C = C
class T a where
t :: a
instance T D where
t = D
instance T C where
t = C
g :: forall a b. (T a, F b a) => IO a
g = do
putStrLn $ f (Proxy :: Proxy (D, a))
putStrLn $ f (Proxy :: Proxy (C, a))
return t
main = (g :: IO D) >> return ()
我遇到以下错误:
y.hs:30:14: error:
• Overlapping instances for F D a arising from a use of ‘f’
Matching instances:
instance [overlappable] F a b -- Defined at y.hs:10:31
instance [overlapping] F a a -- Defined at y.hs:13:30
(The choice depends on the instantiation of ‘a’
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
• In the second argument of ‘($)’, namely
‘f (Proxy :: Proxy (D, a))’
In a stmt of a 'do' block: putStrLn $ f (Proxy :: Proxy (D, a))
In the expression:
do putStrLn $ f (Proxy :: Proxy (D, a))
putStrLn $ f (Proxy :: Proxy (C, a))
return t
|
30 | putStrLn $ f (Proxy :: Proxy (D, a))
| ^^^^^^^^^^^^^^^^^^^^^^^^^
y.hs:31:14: error:
• Overlapping instances for F C a arising from a use of ‘f’
Matching instances:
instance [overlappable] F a b -- Defined at y.hs:10:31
instance [overlapping] F a a -- Defined at y.hs:13:30
(The choice depends on the instantiation of ‘a’
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
• In the second argument of ‘($)’, namely
‘f (Proxy :: Proxy (C, a))’
In a stmt of a 'do' block: putStrLn $ f (Proxy :: Proxy (C, a))
In the expression:
do putStrLn $ f (Proxy :: Proxy (D, a))
putStrLn $ f (Proxy :: Proxy (C, a))
return t
|
31 | putStrLn $ f (Proxy :: Proxy (C, a))
| ^^^^^^^^^^^^^^^^^^^^^^^^^
y.hs:34:9: error:
• Overlapping instances for F b0 D arising from a use of ‘g’
Matching instances:
instance [overlappable] F a b -- Defined at y.hs:10:31
instance [overlapping] F a a -- Defined at y.hs:13:30
(The choice depends on the instantiation of ‘b0’
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
• In the first argument of ‘(>>)’, namely ‘(g :: IO D)’
In the expression: (g :: IO D) >> return ()
In an equation for ‘main’: main = (g :: IO D) >> return ()
|
34 | main = (g :: IO D) >> return ()
| ^
错误提示IncoherentInstances
,但似乎并没有选择正确的实例。我还没有想出什么新尝试。
编辑:仅是为了查看会发生什么,我激活了IncoherentInstances
,但它会导致相同的错误。
编辑2:我将解释该示例如何与我的实际情况结合起来。 g
代表HTML表单。此表单可以返回由T
表示的不同类型。这些不同的类型使用表单中字段的不同子集。 g
中具有putStrLn
和f
的行代表表单中字段的定义。 f
表示根据表单是否返回依赖于该字段的类型来决定是否验证该字段。
例如,表单可能返回类型DocSectionA
或DocSectionB
。一个字段的类型可能为Text
,我们要表达一个特定的字段应该仅在表单返回DocSectionA
时才被验证,而另一个字段应该仅在表单返回{ {1}}。
我希望这会有所帮助。
答案 0 :(得分:4)
我不明白您的T
课程的用途。它似乎并未以有趣/相关的方式实际使用。但是您的f
可以在TypeRep
上使用相等性检查来实现。例如:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
import Data.Type.Equality
import Type.Reflection
data C = C
data D = D
f :: forall a b. (Typeable a, Typeable b) => String
f = case testEquality (typeRep @a) (typeRep @b) of
Just Refl -> "matched"
_ -> "didn't match"
g :: forall a. Typeable a => IO ()
g = do
putStrLn (f @C @a)
putStrLn (f @D @a)
main = g @D
您当然可以通过通常的方式使用代理来避免ScopedTypeVariables和AllowAmbiguousTypes(如果这是您的首选方式)。我使用了Typeable
的新奇花样版本:尽管我们上面没有使用它,但在"matched"
的{{1}}分支中,不仅我们,而且类型检查器都知道f
。
答案 1 :(得分:1)
如今,这就是我们使用歧义类型和类型应用程序的方式。歧义类型允许您拥有不提及类参数的类成员(否则您可以使用代理)。
如果c
,这里0
是a ~ T
,如果1
,则a ~ U
:
{-# LANGUAGE AllowAmbiguousTypes, TypeApplications #-}
data T
data U
class C a where
c :: Int
instance C T where
c = 0
instance C U where
c = 1
main :: IO ()
main = print (c @T) >> print (c @U)
如果在a
不是T
的情况下真的要匹配 any 类型(为什么会这样),则可以使用重叠的实例(和{ {3}}是有关它们如何工作的最佳参考):
{-# LANGUAGE FlexibleInstances #-} -- in addition to the above
instance {-# OVERLAPPABLE #-} C a where
c = 0
main = print (c @String)