2014-06-13 50 views
1

我希望创建一个关联类型族的两个实例,就像这样。当然,这不会编译,我最终在代码中采用了不同的方法。不过,我仍然对这个用例感到好奇。我认为理论上编译器可以允许这样做。即使有多个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扩展可能允许这样做,还是它是非启动器?

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

+0

我不是100%肯定,但我认为即使有型的家庭,你会需要使用newtypes,这主要是'newtype'语义的用途,另一个用途是你可以使类型更明显,比如使用'Network.Socket.PortNumber'而不是'Word16',使得类型签名更加清晰至于这个论点需要什么。 – bheklilr

+3

您是否曾尝试将“Sequence”的类型作为第二个参数添加到类中? –

+0

@Joachim我没有尝试多参数类型的类。这种趋势似乎是使用类型家庭,但也许这是MPTC可以做某些类型家庭无法做到的事情。 – mhwombat

回答

5

类型族比MPTCs弱一点,因为它们只表达类型的函数而不是完整的关系。这对于类型推断具有优势,并且也是MPTC也有fundeps的原因。所以,我认为实现这样的东西不是不可能的,但是它却落在了typechecker功率/重量比的错误一面。

在任何情况下,如果你真正希望这种行为很容易通过足以向MPTCs有

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"