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
fu开发者_运维百科nction.
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.
精彩评论