从幻像类型创建(获取)值实例

mwn*_*wnx 2 haskell gadt phantom-types

我正在使用GADT为货币创建基本维度(如物理维度)系统.尺寸(例如美元,美元/欧元,欧元/美元)表示为幻像类型.我希望能够以例如"10.3USD"或"0EUR"的方式打印一定数量的货币,并使用Show打印例如"10.3USD/EUR"的费率.我不太确定如何解释我的问题,所以我将举例说明我是如何解决它的:

{-# LANGUAGE GADTs #-}

class (Show a) => Currency a where unphantom :: a

data USD = USD deriving Show
data EUR = EUR deriving Show

instance Currency USD where unphantom = USD
instance Currency EUR where unphantom = EUR

data Amount a where
  Amount :: Currency a => Float -> Amount a
instance Show (Amount a) where 
  show (Amount x) = show x ++ show (unphantom :: a)

data Rate a b where
  Rate :: (Currency a, Currency b) => Float -> Rate a b
-- ...
Run Code Online (Sandbox Code Playgroud)

使用此代码,我收到错误

$ ghc example.hs 
[1 of 1] Compiling Main             ( example.hs, example.o )

example.hs:14:37:
    Could not deduce (Currency a1) arising from a use of `unphantom'
    from the context (Currency a)
      bound by a pattern with constructor
                 Amount :: forall a. Currency a => Float -> Amount a,
               in an equation for `show'
      at example.hs:14:9-16
    Possible fix:
      add (Currency a1) to the context of
        an expression type signature: a1
        or the data constructor `Amount'
        or the instance declaration
    In the first argument of `show', namely `(unphantom :: a)'
    In the second argument of `(++)', namely `show (unphantom :: a)'
    In the expression: show x ++ show (unphantom :: a)
Run Code Online (Sandbox Code Playgroud)

我必须说我不明白为什么在这种情况下编译器在a1我指定时讨论类型a.

当然,我想避免代表Haskell的类型系统之外的尺寸,因为这增加了额外的样板代码对我来说,是据我可以告诉理论上不必要的(即编译器应该有足够的信息来推断如何显示的金额或编译时的速率)(并在运行时添加一点开销).

J. *_*son 5

使用ScopedTypeVariables和您的代码按原样编译.

特别是,没有ScopedTypeVariables你写的时候

instance Show (Amount a) where 
  show (Amount x) = show x ++ show (unphantom :: a)
Run Code Online (Sandbox Code Playgroud)

aunphantom :: a新鲜的,并没有取得与统一ainstance Show (Amount a) where.开启ScopedTypeVariables迫使它统一起来.