Haskell entries are actually not “less structural” than the rest of the type system. Each type is either fully defined or "definitely vague" (i.e. defined using a class).
To allow both HRec and HRec2 to f , you have several options:
Algebraic types:
Here you define the HRec and HRec2 as part of the HRec type:
data HRec = HRec { x :: Int, y :: Bool } | HRec2 { p :: Int, q :: Bool } foo :: HRec -> Bool
(alternately and perhaps more idiomatically :)
data HRecType = Type1 | Type2 data HRec = HRec { hRecType :: HRecType, x :: Int, y :: Bool }
Type classes
Here you define foo as capable of accepting any type as input if an instance of typeclass was written for this type:
data HRec = HRec { x :: Int, y :: Bool } data HRec2 = HRec2 { p :: Int, q :: Bool } class Flexible a where foo :: a -> Bool instance Flexible HRec where foo (HRec a _) = a == 5 -- or whatever instance Flexible HRec2 where foo (HRec2 a _) = a == 5
Using type classes allows you to go beyond the usual structural input - you can accept types that have the necessary information built into them, even if the types do not look the same outward, for example:
data Foo = Foo { a :: String, b :: Float } data Bar = Bar { c :: String, d :: Integer } class Thing a where doAThing :: a -> Bool instance Thing Foo where doAThing (Foo xy) = (x == "hi") && (y == 0) instance Thing Bar where doAThing (Bar xy) = (x == "hi") && ((fromInteger y) == 0)
We can run fromInteger (or any arbitrary function) to get the data we need from what we have!
amindfv
source share