从上下文“显示(a,b)”

时间:2016-09-22 13:03:30

标签: haskell typeclass

正如标题所说,我有兴趣在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的非侵入性(或仅仅是不同的)方式。如果没有,你能解释为什么会出现这个问题吗?

3 个答案:

答案 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包中的constraintsa :=> b表示某处有instance a => b

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 kDict 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