Folding over a polymorphic list in Haskell

I have a collection of records spread across a number of types in a large Haskell application that reference each other. All of the types involved implement a common typeclass. The typeclass contains functions that work over a variable and all of its children, very much like uniplate's para function.

This is a simplified code sample of what I'd like to build. Is it possible (and reasonable) to get generic functionality to fold over record fields that implement a given typeclass in 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

But generic enough to work with an arbitrary typeclass?

{-# 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}

Compiling this with GHC 7.2.1 (obviously) fails:

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}

You've hit the Existential Antipattern. You shouldn't be using typeclasses for anything except cases when you need compiler to guess the type for you. List of values of type x will stay the list of values of type x no matter what typeclasses you will implement, and you can't break the type system here.

You can:

  • Use an ad-hoc box type as suggested above. This is just plain ugly.

  • Implement generic interfaces with message-passing.

    data Foo = Foo { fooConst :: Int }

    bar = Foo 2

    baz = Foo 3


  • been a while..

    Have you tried existentially quantified data constructors?

    data Foo = forall a. MyTypeClass a => Bar [a]
    
    func (Bar l) = map typeClassMember a
    

    now, func will work with anything of type Foo, which hides the inner type.

    链接地址: http://www.djcxy.com/p/43088.html

    上一篇: HLists中的修复类型推断

    下一篇: 在Haskell中折叠多态列表