Свертывание над полиморфным списком в Haskell - PullRequest
14 голосов
/ 14 октября 2011

У меня есть коллекция записей, распределенных по нескольким типам в большом приложении на Haskell, которые ссылаются друг на друга. Все задействованные типы реализуют общий класс типов. Класс типов содержит функции, которые работают с переменной и всеми ее дочерними элементами, во многом как функция uniplate para.

Это упрощенный пример кода, который я хотел бы построить. Возможно ли (и разумно) получить универсальную функциональность для сворачивания полей записи, которые реализуют данный класс типов в GHC ...

{-# LANGUAGE RankNTypes #-}

myPara :: forall a r . (Data a, Foo a)
       => (forall b . Foo b => b -> [r] -> r)
       -> a
       -> r

-- or as a fold
myFold :: forall a r . (Data a, Foo a)
       => (forall b . Foo b => r -> b -> r)
       -> r
       -> b
       -> r

Но достаточно ли он универсален для работы с произвольным классом типов?

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Generics.Uniplate.Data

class Foo a where 
  fooConst :: a -> Int

data Bar = Bar {barBaz :: Baz} deriving (Typeable, Data)

instance Foo Bar where
  fooConst _ = 2

data Baz = Baz {barBar :: Bar} deriving (Typeable, Data)

instance Foo Baz where
  fooConst _ = 3

func :: Int
func = foldl (\ x y -> x + fooConst y) 0 instances where
  instances :: forall a . (Data a, Foo a) => [a]
  instances = universeBi bar
  bar = Bar{barBaz = baz}
  baz = Baz{barBar = bar}

Компиляция с помощью GHC 7.2.1 (очевидно) завершается неудачей:

Repro.hs:21:42:
    Ambiguous type variable `a0' in the constraints:
      (Data a0) arising from a use of `instances' at Repro.hs:21:42-50
      (Foo a0) arising from a use of `instances' at Repro.hs:21:42-50
    Probable fix: add a type signature that fixes these type variable(s)
    In the third argument of `foldl', namely `instances'
    In the expression: foldl (\ x y -> x + fooConst y) 0 instances
    In an equation for `func':
        func
          = foldl (\ x y -> x + fooConst y) 0 instances
          where
              instances :: forall a. (Data a, Foo a) => [a]
              instances = universeBi bar
              bar = Bar {barBaz = baz}
              baz = Baz {barBar = bar}

Ответы [ 2 ]

1 голос
/ 09 марта 2012

Вы попали в Existential Antipattern. Вы не должны использовать классы типов ни для чего, кроме случаев, когда вам нужен компилятор, чтобы угадать тип для вас.Список значений типа x будет оставаться в списке значений типа x независимо от того, какие классы типов вы будете реализовывать, и вы не можете нарушить систему типов здесь.

Вы можете:

  1. Используйте специальный тип коробки, как предложено выше.Это просто ужасно.

  2. Реализация универсальных интерфейсов с передачей сообщений.

    data Foo = Foo {fooConst :: Int}

    bar =Foo 2

    baz = Foo 3

0 голосов
/ 27 ноября 2011

давно.

Пробовали ли вы количественно определенные конструкторы данных?

data Foo = forall a. MyTypeClass a => Bar [a]

func (Bar l) = map typeClassMember a

теперь func будет работать с любым типом Foo, который скрывает внутренний тип.

...