类型类的实例有两种不同的方式

时间:2014-06-13 14:30:26

标签: haskell

我希望创建一个关联类型系列的两个实例,就像这样。当然,这不会编译,我最终在我的代码中采用了不同的方法。但是,我仍然对这个用例感到好奇。我认为理论上编译器可以允许这样做。即使可以选择putget的多个实例,结果类型也可以清楚地说明需要哪个实例。

{-# LANGUAGE TypeFamilies #-}
import Data.Word

class Genetic g where
  type Sequence g :: *
  -- | Writes a gene to a sequence.
  put :: Sequence g -> g -> Sequence g
  -- | Reads the next gene in a sequence.
  get :: Sequence g -> (g, Sequence g)

data SampleGene = Variant1 | Variant2 deriving Show

instance Genetic SampleGene where
  type Sequence SampleGene = [Bool]
  put xs Variant1 = True : xs
  put xs Variant2 = False : xs
  get (True:xs) = (Variant1, xs)
  get (False:xs) = (Variant2, xs)

instance Genetic SampleGene where
  type Sequence SampleGene = [Word8]
  put xs Variant1 = 0 : xs
  put xs Variant2 = 1 : xs
  get (0:xs) = (Variant1, xs)
  get (1:xs) = (Variant2, xs)
  get _ = error "coding error"

main = do
  putStrLn "Working with Bool sequences"
  let xs = put [] Variant1 :: [Bool]
  let (g,ys) = get xs :: (SampleGene, [Bool])
  putStrLn $ "Read " ++ show g
  putStrLn "Working with Bool sequences"
  let xs = put [] Variant1 :: [Word8]
  let (g,ys) = get xs :: (SampleGene, [Word8])
  putStrLn $ "Read " ++ show g

我的问题是:

  1. 在Haskell中有没有办法做到这一点? (除了newtype包装器 - 我希望我的库的用户能够直接使用Haskell基类型。)

  2. 如果没有,为什么不呢?即,我违反了什么规则,或者我打的类型系统有什么限制?我想在我写的一篇论文中解释这一点,但我正在努力了解类型系统如何在幕后工作。因此,如果您在答案中使用技术术语,那么我可以在阅读中注意这些术语。

  3. 理论上,未来的Haskell扩展是否有可能允许这样做,或者它是非启动者?

  4. 我见过In Haskell, is there any way to express that a type should be an instance of a typeclass in more than one way?,这与我的问题非常相似。但是,我的重点是努力更好地理解类型系统。

1 个答案:

答案 0 :(得分:5)

类型族比MPTC弱一点,因为它们仅表示类型的函数而不是完整关系。这对于类型推断具有优势,也是MPTC也有趣的原因。因此,我不认为实现这样的事情是不可能的,但它却落后于类型检测器功率/重量比的错误。

无论如何,如果你真的想要这种行为,通过MPTC很容易

class Genetic s g where
  -- | Writes a gene to a sequence.
  put :: s -> g -> s
  -- | Reads the next gene in a sequence.
  get :: s -> (g, s)

instance Genetic [Bool] SampleGene where
  put xs Variant1 = True  : xs
  put xs Variant2 = False : xs
  get (True  : xs) = (Variant1, xs)
  get (False : xs) = (Variant2, xs)

instance Genetic [Int] SampleGene where
  put xs Variant1 = 0 : xs
  put xs Variant2 = 1 : xs
  get (0 : xs) = (Variant1, xs)
  get (1 : xs) = (Variant2, xs)
  get _        = error "boo"