正如标题所说,我有兴趣在Show a
的上下文中使用Show (a,b)
。 GADT很容易出现这个问题如下:
data PairOrNot a where
Pair :: (b,c) -> PairOrNot (b,c)
Not :: a -> PairOrNot a
showFirstIfPair :: Show a => PairOrNot a -> String
showFirstIfPair (Not a) = show a
showFirstIfPair (Pair (b,c)) = show b
错误是:
Could not deduce (Show b) arising from a use of ‘show’
from the context (Show a)
bound by the type signature for
showFirstIfPair :: Show a => PairOrNot a -> String
at app/Main.hs:24:20-50
or from (a ~ (b, c))
bound by a pattern with constructor
Pair :: forall b c. (b, c) -> PairOrNot (b, c),
in an equation for ‘showFirstIfPair’
at app/Main.hs:26:18-27
Possible fix:
add (Show b) to the context of the data constructor ‘Pair’
In the expression: show b
In an equation for ‘showFirstIfPair’:
showFirstIfPair (Pair (b, c)) = show b
我认为实例声明instance (Show a, Show b) => Show (a,b)
证明Show element
,但我也可以想象问题也与类型机制在运行时如何实现有关。
我发现如果我们可以修改课程定义,可以通过以下方式解决这个问题:
class Show' a where
show' :: a -> String
unpair :: a -> Dict (a ~ (b,c)) -> Dict (Show' b, Show' c)
-- An example non-pair instance
instance Show' Int where
show' i = ""
unpair = undefined -- This is OK, since no one can construct Dict (Int ~ (b,c))
instance (Show' a, Show' b) => Show' (a,b) where
show' (a,b) = ""
unpair _ Dict = Dict -- In this context we have access to Show' for elems
然后在使用网站上,我们明确地获取字典:
showFirstIfPair :: Show' a => PairOrNot a -> String
showFirstIfPair (Not a) = show' a
showFirstIfPair (Pair a@(b,c)) =
case unpair a Dict of -- This is a Dict (a~(b,c))
Dict -> show' b -- This is Dict (Show' b,Show' c)
我想知道是否存在获取Show element
的非侵入性(或仅仅是不同的)方式。如果没有,你能解释为什么会出现这个问题吗?
答案 0 :(得分:2)
如果您不介意<Grid x:Name="BasicButtonGrid"
VerticalOptions="End">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.MinimumHeightRequest>
<OnIdiom x:TypeArguments="x:Double"
Phone="40"
Tablet="70"/>
</Grid.MinimumHeightRequest>
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBasicButtonTapped"/>
</Grid.GestureRecognizers>
<BoxView BackgroundColor="Blue"
VerticalOptions="EndAndExpand"
InputTransparent="True"
Grid.Row="0"
Grid.Column="0"/>
<Label Text="Basic Button with long text"
TextColor="White"
FontSize="Medium"
Grid.Row="0"
Grid.Column="0"/>
</Grid>
必须始终是b
的实例的限制,那么这是一个简单的解决方案:
Show
答案 1 :(得分:1)
我一直在努力想知道自己是否能想出更好的东西,这是我能想到的最好的东西:
在我最初的尝试中,我将Show'
类型类与对的实例声明相结合。即使我找不到修改类型类的方法,但我至少设法对任何实例声明进行了概括。
正如问题中所指出的那样instance (Show a, Show b) => Show (a,b)
是一种单向含义,但我认为它可以应用于另一个方向,因为没有重叠的实例。不幸的是,GHC并不依赖于此,但我们可以断言自己。我的直觉可以转化为代码:(:=>
来自Data.Constraint
包中的constraints
,a :=> b
表示某处有instance a => b
1} p>
class Show' a where
show' :: a -> String
noOverlap :: (b :=> Show' a) => Proxy a -> Dict b
此处,noOverlap
函数是一个承诺,如果您可以找到引发实例b
的约束Show' a
,我可以证明b
给定Show' a
1}}。此承诺相当于声明Show'
现在,我们需要一个辅助函数来实际实现noOverlap
basedOn :: forall k a. (k :=> Show' a, k) =>
(forall b. (b :=> Show' a) => Proxy a -> Dict b)
basedOn _ = unsafeCoerce (Dict :: Dict k)
此功能的作用是,如果您在具有实例k :=> Show' a
的上下文中调用它,它将返回一个函数,该函数将为任何返回Dict b
实例b :=> Show' a
。我们必须使用unsafeCoerce
来说服GHC Dict k
和Dict b
是相同的,但据我所知,这是对safeafeCoerce的安全使用,因为函数依赖{{1} 1}}确保给定a :=> b | b -> a
只能有k :=> Show' a
的一个实例。
现在,给定这个帮助器,这是你如何定义实例
Show' a
我们必须手动定义-- An example non-pair instance
instance () :=> Show' Int where ins = Sub Dict
instance Show' Int where
show' i = ""
noOverlap = basedOn
instance (Show' a, Show' b) :=> Show' (a,b) where ins = Sub Dict
instance (Show' a, Show' b) => Show' (a,b) where
show' (a,b) = ""
noOverlap = basedOn -- GHC does all the plumbing here
实例,因为GHC不会自动执行,但是那里没有错误的余地。如果我们在手动定义的:=>
的左侧给出一个太弱的约束,:=>
将无法编译,如果我们给出一个太强的约束,那么ins = Sub Dict
将无法编译,所以编译器强制使用该样板。
然后我们可以使用noOverlap = basedOn
,如下所示:
noOverlap
好消息是,现在我们也可以从showFirstIfPair :: Show' a => PairOrNot a -> String
showFirstIfPair (Not a) = show' a
showFirstIfPair (Pair a@(b,c)) =
-- In this context we have (Show' b, Show' c) :=> Show' a
case noOverlap Proxy of -- This is a Proxy a
Dict -> show' b -- This is a Dict (Show' b, Show' c)
转到Show' [a]
或任何其他实例声明。
注意:您需要Show' a
来编译这些。
答案 2 :(得分:1)
这是user2297560的概括,它并不需要在GADT中进行硬编码Show
:
{-# LANGUAGE ConstraintKinds, KindSignatures #-}
import GHC.Exts (Constraint)
data PairOrNot (cl :: * -> Constraint) (a :: *) where
Pair :: (cl b, cl c) => (b,c) -> PairOrNot (b,c)
Not :: cl a => a -> PairOrNot a
然后你可以
showFirstIfPair :: PairOrNot Show a -> String
showFirstIfPair (Not a) = show a
showFirstIfPair (Pair (b,c)) = show b