在Haskell的Type Classes中记录选择器

时间:2013-07-20 14:53:26

标签: haskell types record typeclass

我想使用几个默认方法实现Type Class,但我收到错误,我无法在record selectors定义中使用type classes

以下代码基本上创建了type class,它定义了add函数,它应该为某些repr的{​​{1}}记录添加一个元素。这是代码:

data type

编译器抛出错误:

import qualified Data.Graph.Inductive     as DG

class Graph gr a b where
    empty :: DG.Gr a b
    empty = DG.empty

    repr :: gr -> DG.Gr a b

    -- following function declaration does NOT work:
    add :: a -> gr -> gr
    add el g = g{repr = DG.insNode el $ repr g}

是否可以在Haskell中声明此类方法?

澄清

我需要这样的设计,因为我有一些repr is not a record selector In the expression: g {repr = DG.insNode el $ repr g} In an equation for add: add el g = g {repr = DG.insNode el $ repr g} ,它以相同的方式表现。可以说,我们得到了data typesAB C。他们每个人都应该有一个记录data types,其中repr :: DG.Gr a ba对于bAB各有所不同。

CAB共享相同的功能,例如Cadd(基本上添加或删除要记录的元素{{1} })。如果这些数据类型共享许多函数,那么在delete中实现函数并生成此repr的实例是有意义的 - 这些函数将自动为每个type class实现。< / p>

另外我希望其中一些type class(假设我希望data type)在调用data types函数时表现得略有不同。在为B add instance进行type class时,很容易实现此行为。

2 个答案:

答案 0 :(得分:3)

  1. 记录更新语法

     <record-instance> { <record-field-name> = ..., ... }
    

    <record-instance>已知代数数据类型的实例/术语时(因为<record-field-name>是已知字段),在您的代码中它只是一些( ad-hoc)多态参数gr,因此您首先需要将gr转换为Gr,然后更新它,然后再...

  2. 我认为grGr在某种意义上应该是等价的,即我们需要repr的反函数,比如iface,才能够实施add

  3. 以下是一个例子:

    {-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
    
    data Gr a b = Gr { _internal :: [(a, b)] } deriving ( Show, Read )
    
    class Graph gr a b where
    
      repr :: gr -> Gr a b
      iface :: Gr a b -> gr
    
      -- iface . repr == id {gr}
      -- repr . iface == id {Gr a b}
    
      -- add element via "interface" (get a representation via @repr@, update it, and then 
      -- return an interface back with @iface@)
      add :: (a, b) -> gr -> gr
      add el g = let r = repr g in iface r { _internal = el : _internal r }
      -- or
      add el = iface . insNode el . repr where
        insNode x (Gr xs) = Gr (x : xs) -- or whatever
    
    instance Graph String Int Int where
      repr = read
      iface = show
    
    test :: String
    test = add (1 :: Int, 2 :: Int) "Gr { _internal = [] }"
    -- test => "Gr {_internal = [(1,2)]}"
    

    如果某些数据类型AB 聚合 Gr a b(这样我们就无法为repr编写反转),那么我们可以这样做:

    {-# LANGUAGE MultiParamTypeClasses #-}
    
    data Gr a b = Gr [(a, b)] deriving ( Show )
    
    class Graph gr a b where
    
      repr :: gr -> Gr a b
    
      update :: gr -> (Gr a b -> Gr a b) -> gr
      -- 2: update :: gr -> Gr a b -> gr
    
      add :: (a, b) -> gr -> gr
      add el g = update g $ insNode el
        -- 2: update g (insNode el $ repr g)
        where insNode x (Gr xs) = Gr (x : xs)
    
    data A = A { _aRepr :: Gr Char Char, _aRest :: Char } deriving ( Show )
    data B = B { _bRepr :: Gr Int Int, _bRest :: Int } deriving ( Show )
    
    instance Graph A Char Char where
      repr = _aRepr
      update r f = r { _aRepr = f $ _aRepr r }
      -- 2: update r g = r { _aRepr = g }
    
    instance Graph B Int Int where
      repr = _bRepr
      update r f = r { _bRepr = f $ _bRepr r }
      -- 2: update r g = r { _bRepr = g }
    
    testA :: A
    testA = add ('1', '2') $ A (Gr []) '0'
    -- => A {_aRepr = Gr [('1','2')], _aRest = '0'}
    
    testB :: B
    testB = add (1 :: Int, 2 :: Int) $ B (Gr []) 0
    -- => B {_bRepr = Gr [(1,2)], _bRest = 0}
    

    也可以在这里使用lenses

    {-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-}
    
    import Control.Lens
    
    data Gr a b = Gr [(a, b)] deriving ( Show )
    
    insNode :: (a, b) -> Gr a b -> Gr a b
    insNode x (Gr xs) = Gr (x : xs)
    
    class Graph gr a b where
      reprLens :: Simple Lens gr (Gr a b)
    
    add :: Graph gr a b => (a, b) -> gr -> gr
    add el = reprLens %~ insNode el
    
    data A = A { _aRepr :: Gr Char Char, _aRest :: Char } deriving ( Show )
    data B = B { _bRepr :: Gr Int Int, _bRest :: Int } deriving ( Show )
    
    makeLenses ''A
    makeLenses ''B
    
    instance Graph A Char Char where
      reprLens = aRepr
    
    instance Graph B Int Int where
      reprLens = bRepr
    
    main :: IO ()
    main = do
      let a = A (Gr []) '0'
          b = B (Gr []) 0
      print $ add ('0', '1') a
      print $ add (0 :: Int, 1 :: Int) b
    -- A {_aRepr = Gr [('0','1')], _aRest = '0'}
    -- B {_bRepr = Gr [(0,1)], _bRest = 0}
    

答案 1 :(得分:0)

您可以尝试这样的事情(使用元组列表作为示例,而不是DG

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances #-}

class MyClass g a b | g -> a b where
      extract :: g -> [(a,b)]
      construct :: [(a,b)] -> g 

      empty :: g
      empty = construct []

      add :: (a,b) -> g  -> g
      add i d = construct $  [i] ++ (extract d) 

data A  = A {reprA :: [(Int,Int)]}

instance MyClass A Int Int  where
         extract = reprA
         construct = A

data B  = B {reprB :: [(String,String)]}

instance MyClass B String String  where
         extract = reprB
         construct = B