如何根据不相关的类型选择常量类型的值?
How to chose a value of a constant type based on an unrelated type?
好的,所以在我想要进行更改之前,我得到了以下简化的工作示例:
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 形式。这种形式可以 return 表示的不同类型 T
。这些不同的类型使用表单中不同的字段子集。 g
中具有 putStrLn
和 f
的行表示表单中字段的定义。 f
表示根据表单是否 returning 依赖于它的类型来决定是否验证字段。
例如,表单可能 return 类型 DocSectionA
或 DocSectionB
。一个字段可能是 Text
类型,我们想表达一个特定的字段应该只在表单 returning a DocSectionA
时才被验证,而另一个字段只应该在 DocSectionA
时被验证表格是 returning a DocSectionB
.
希望对您有所帮助。
下面是我们现在如何处理不明确的类型和类型应用程序。模棱两可的类型允许您拥有 class 不提及 class 参数的成员(否则您可以使用代理)。
此处 c
如果 a ~ T
则为 0
,或 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 类型(虽然你为什么会这样),你可以使用重叠实例(GHC manual 是关于它们如何工作的最佳参考):
{-# LANGUAGE FlexibleInstances #-} -- in addition to the above
instance {-# OVERLAPPABLE #-} C a where
c = 0
main = print (c @String)
我不明白你的 T
class 是干什么用的。它似乎并没有真正以 interesting/relevant 的方式使用。但是您的 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
的新花式版本:虽然我们没有在上面使用它,但在 f
的 "matched"
分支中,不仅我们而且类型检查器都知道a ~ b
.
好的,所以在我想要进行更改之前,我得到了以下简化的工作示例:
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 形式。这种形式可以 return 表示的不同类型 T
。这些不同的类型使用表单中不同的字段子集。 g
中具有 putStrLn
和 f
的行表示表单中字段的定义。 f
表示根据表单是否 returning 依赖于它的类型来决定是否验证字段。
例如,表单可能 return 类型 DocSectionA
或 DocSectionB
。一个字段可能是 Text
类型,我们想表达一个特定的字段应该只在表单 returning a DocSectionA
时才被验证,而另一个字段只应该在 DocSectionA
时被验证表格是 returning a DocSectionB
.
希望对您有所帮助。
下面是我们现在如何处理不明确的类型和类型应用程序。模棱两可的类型允许您拥有 class 不提及 class 参数的成员(否则您可以使用代理)。
此处 c
如果 a ~ T
则为 0
,或 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 类型(虽然你为什么会这样),你可以使用重叠实例(GHC manual 是关于它们如何工作的最佳参考):
{-# LANGUAGE FlexibleInstances #-} -- in addition to the above
instance {-# OVERLAPPABLE #-} C a where
c = 0
main = print (c @String)
我不明白你的 T
class 是干什么用的。它似乎并没有真正以 interesting/relevant 的方式使用。但是您的 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
的新花式版本:虽然我们没有在上面使用它,但在 f
的 "matched"
分支中,不仅我们而且类型检查器都知道a ~ b
.