|
|
Line 1: |
Line 1: |
− | == Untypechecking ==
| |
| | | |
− | Converting from a type to a term.
| |
− |
| |
− | <pre>
| |
− | [Haskell] De-typechecker: converting from a type to a term
| |
− |
| |
− | oleg at pobox.com oleg at pobox.com
| |
− | Tue Mar 1 03:13:08 EST 2005
| |
− |
| |
− | * Next message: [Haskell] Re: Type of y f = f . f
| |
− | * Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
| |
− |
| |
− | -----------------------------------------------------------------------------------------------------
| |
− |
| |
− | This message presents polymorphic functions that derive a term for a
| |
− | given type -- for a class of fully polymorphic functions: proper and
| |
− | improper combinators. This is better understood on an example:
| |
− |
| |
− | rtest4 f g = rr (undefined::(b -> c) -> (a -> b) -> a -> c) HNil f g
| |
− | *HC> rtest4 (:[]) Just 'x'
| |
− | [Just 'x']
| |
− | *HC> rtest4 Just Right True
| |
− | Just (Right True)
| |
− |
| |
− | We ask the Haskell typechecker to derive us a function of the
| |
− | specified type. We get the real function, which we can then apply to
| |
− | various arguments. The return result does behave like a `composition'
| |
− | -- which is what the type specifies. Informally, we converted from
| |
− | `undefined' to defined.
| |
− |
| |
− | It must be emphasized that no modifications to the Haskell compiler are
| |
− | needed, and no external programs are relied upon. In particular,
| |
− | however surprising it may seem, we get by without `eval' -- because
| |
− | Haskell has reflexive facilities already built-in.
| |
− |
| |
− | This message contains the complete code. It can be loaded as it is
| |
− | into GHCi -- tested on GHCi 6.2.1 or GHCi snapshot 6.3.20041106. It
| |
− | may take a moment or two to load though. Commenting our tests speeds
| |
− | up the loading.
| |
− |
| |
− |
| |
− | This message presents two different converters from a type to a term.
| |
− | Both derive a program, a term, from its specification, a type -- for
| |
− | a class of fully polymorphic functions. The first converter has just
| |
− | been demonstrated. It is quite limited in that the derived function
| |
− | must be used `polymorphically' -- distinct type variables must be
| |
− | instantiated to different types (or, the user should first
| |
− | instantiate their types and then derive the term). The second
| |
− | converter is far more useful: it can let us `visualize' what a
| |
− | function with a particular type may be doing. For example, it might
| |
− | not be immediately clear what the function of the type
| |
− | (((a -> b -> c) -> (a -> b) -> a -> c) ->
| |
− | (t3 -> t1 -> t2 -> t3) -> t) -> t)
| |
− |
| |
− | is. The test (which is further down) says
| |
− |
| |
− | test9 = reify (undefined::(((a -> b -> c) -> (a -> b) -> a -> c) ->
| |
− | (t3 -> t1 -> t2 -> t3) -> t) -> t) gamma0
| |
− |
| |
− | *HC> test9
| |
− | \y -> y (\d h p -> d p (h p)) (\d h p -> d)
| |
− |
| |
− | that is, the function in question is one of the X combinators. It is
| |
− | an improper combinator.
| |
− |
| |
− | A particular application is converting a point-free function into the
| |
− | pointful form to really understand the former. For example, it might
| |
− | take time to comprehend the following expression
| |
− |
| |
− | pz = (((. head . uncurry zip . splitAt 1 . repeat) . uncurry) .) . (.) . flip
| |
− |
| |
− | It is derived by Stephan Hohe in
| |
− | http://www.haskell.org/pipermail/haskell-cafe/2005-February/009146.html
| |
− |
| |
− | Our system says
| |
− | test_pz = reify (undefined `asTypeOf` pz) gamma0
| |
− | *HC> test_pz
| |
− | \h p y -> h y (p y)
| |
− |
| |
− | So, pz is just the S combinator.
| |
− |
| |
− | As an example below shows, an attempt to derive a term for a type
| |
− | a->b expectedly fails. The type error message essentially says that
| |
− | a |- b is underivable.
| |
− |
| |
− |
| |
− | Our system solves type habitation for a class of functions with
| |
− | polymorphic types. From another point of view, the system is a prover
| |
− | in the implication fragment of intuitionistic logic. Essentially we
| |
− | turn a _type_ into a logical program -- a set of Horn clauses -- which
| |
− | we then solve by SLD resolution. It is gratifying to see that
| |
− | Haskell typeclasses are up to that task.
| |
− |
| |
− |
| |
− | The examples above exhibit fully polymorphic types -- those with
| |
− | *uninstantiated* -- implicitly universally quantified -- type
| |
− | variables. That is, our typeclasses can reify not only types but also
| |
− | type schemas. The ability to operate on and compare *unground* types
| |
− | with *uninstantiated* type variables is often sought but rarely
| |
− | attained. The contribution of this message is the set of primitives
| |
− | for nominal equality comparison and deconstruction of unground
| |
− | types. Earlier these tools were used to implement nested monadic
| |
− | regions in the type-safe manner without imposing the total order on
| |
− | the regions. The monadic regions are described in the paper by M. Fluet
| |
− | and G. Morrisett, ICFP04.
| |
− |
| |
− | The rest of this message is as follows:
| |
− |
| |
− | - equality predicate of type schemas
| |
− | - function types as logic programs. Solving type habitation
| |
− | by SLD resolution
| |
− | - more examples
| |
− | - code
| |
− | -- class Resolve: SLD resolution
| |
− |
| |
− | * Equality predicate of type schemas
| |
− |
| |
− | This is a short remark on the equality predicate of type schemas. In
| |
− | more detail, this topic will be described elsewhere.
| |
− |
| |
− | To obtain the equality, we need the following overlapping instances
| |
− | extensions
| |
− |
| |
− | > {-# OPTIONS -fglasgow-exts #-}
| |
− | > {-# OPTIONS -fallow-undecidable-instances #-}
| |
− | > {-# OPTIONS -fallow-overlapping-instances #-}
| |
− | > {-# OPTIONS -fallow-incoherent-instances #-}
| |
− | >
| |
− | > module HC where
| |
− |
| |
− |
| |
− | We must remark that -fallow-overlapping-instances and
| |
− | -fallow-incoherent-instances extensions are used *exclusively* for the
| |
− | type equality testing. With these extensions and the following declarations
| |
− |
| |
− | class C a where op:: a -> Int
| |
− | instance C a where ...
| |
− | instance C Bool where ...
| |
− | data W = forall a. W a
| |
− |
| |
− | the expression
| |
− | case W () of W x -> op x
| |
− |
| |
− | is well-typed with the "instance C a" chosen. In the context of TypeEq,
| |
− | that behavior guarantees that a quantified variable is equal only to itself
| |
− | and nothing else. So, the use of overlapping instances in this code
| |
− | is well-defined and sound.
| |
− |
| |
− | The existence of this remarkable feature has been pointed out by Simon
| |
− | Peyton-Jones, who implemented it.
| |
− |
| |
− | The code at the end of this message defines nominal equality on
| |
− | quantified type variables. A quantified variable is equal only to
| |
− | itself. It is useful to think of such type variables as being
| |
− | Skolemized. Fortunately, GHC kindly does all the Skolemization for
| |
− | us. For example,
| |
− |
| |
− | *FP> type'eq id id
| |
− | HFalse
| |
− |
| |
− | because the type of the identity function "a->a" means "forall a. a
| |
− | ->a", which, after skolemization, becomes "sk1 -> sk1". The type of
| |
− | the second `id' in the above equation becomes "sk2 -> sk2" -- thus the
| |
− | types are not equal. Indeed, if we ask GHC to show us the inferred
| |
− | type of the above expression
| |
− |
| |
− | *FP> :t type'eq id id
| |
− | type'eq id id :: forall a a1 b. (TypeEq (a -> a) (a1 -> a1) b) => b
| |
− |
| |
− | we can see the Skolemization very clearly.
| |
− |
| |
− | OTH,
| |
− | *FP> let f x = (x,x) in case f id of (u,v) -> type'eq u v
| |
− | HTrue
| |
− | because
| |
− | *FP> :t let f x = (x,x) in case f id of (u,v) -> type'eq u v
| |
− | let f x ... :: forall a b. (TypeEq (a -> a) (a -> a) b) => b
| |
− |
| |
− | that is, the types of both `id' are unified (yet they remain unground)
| |
− |
| |
− | * Two type-to-term reifiers
| |
− |
| |
− | As we have mentioned, we introduce two type reifiers. The first one converts
| |
− | a type to the following term:
| |
− |
| |
− | > data Term = Var Int | L Int Term | A Term Term -- deriving Show
| |
− |
| |
− | The custom Show instance is at the end of this message. The second reifier
| |
− | converts a type to a true Haskell term. The difference between the reifiers
| |
− | is akin to the difference between a homogeneous list and a heterogeneous
| |
− | list (HList). The former reifier is far easier to explain.
| |
− |
| |
− | * Function types as logic programs. Solving type habitation by SLD resolution
| |
− |
| |
− | To solve the type habitation problem -- to find a term (proof) for a
| |
− | proposition expressed as a type -- we use SLD resolution. It may be
| |
− | helpful to think of the whole process as converting a type
| |
− | (proposition) into a Horn-clause logical program, and solving that
| |
− | program using a Prolog-style evaluator. Here are a few examples of
| |
− | types and the corresponding logical programs.
| |
− |
| |
− | The type a->b->a after applying the implication elimination rule twice
| |
− | yields the following program:
| |
− |
| |
− | t2t(a, var(1)).
| |
− | t2t(b, var(2)).
| |
− | ?- t2t(a,X), Term = lambda(1,(lambda(2,X))).
| |
− | % solution: lambda(1, lambda(2, var(1))), or \x y -> x
| |
− |
| |
− | The type "(b -> c) -> (a -> b) -> a -> c" (of the composition function)
| |
− | corresponds to the following program:
| |
− |
| |
− | t2t(c,app(var(1),X)) :- t2t(b,X).
| |
− | t2t(b,app(var(2),X)) :- t2t(a,X).
| |
− | t2t(a,var(3)).
| |
− | ?- t2t(c,X), Term = lambda(1,lambda(2,lambda(3,X))).
| |
− | % solution:
| |
− | % lambda(1, lambda(2, lambda(3, app(var(1), app(var(2), var(3))))))
| |
− | % or \x y z -> x (y z)
| |
− |
| |
− |
| |
− | The type of one of the X combinators [1] (which is an improper combinator)
| |
− | forall t a b c t1 t2 t3.
| |
− | (((a -> b -> c) -> (a -> b) -> a -> c) -> (t3 -> t1 -> t2 -> t3) -> t)
| |
− | -> t
| |
− |
| |
− | corresponds to the logical program
| |
− |
| |
− | t2t(t, app(app(var(1),X),Y)) :- t2t(u1,X), t2t(u2,Y).
| |
− |
| |
− | % u1 denotes (a -> b -> c) -> (a -> b) -> a -> c
| |
− | t2t(u1,X) :- t2t(c,Y), X = lambda(3,lambda(4,lambda(5,Y))).
| |
− | t2t(c,app(app(var(3),X),Y)) :- t2t(a,X), t2t(b,Y).
| |
− | t2t(b,app(var(4),X)) :- t2t(a,X).
| |
− | t2t(a,var(5)).
| |
− |
| |
− | % u2 denotes t3 -> t1 -> t2 -> t3
| |
− | t2t(u2,X) :- t2t(t3,Y), X = lambda(6,lambda(7,lambda(8,Y))).
| |
− | t2t(t3,var(6)).
| |
− | t2t(t1,var(7)).
| |
− | t2t(t2,var(8)).
| |
− |
| |
− | ?- t2t(t,X), Term = lambda(1,X).
| |
− |
| |
− | The solution to the latter is, when printed nicely,
| |
− | \f -> f (\a b c -> (a c) (b c)) (\x y z -> x)
| |
− |
| |
− |
| |
− | We construct such programs and solve them with the ordinary SLD resolution
| |
− | -- only using Haskell typeclasses rather than Prolog.
| |
− |
| |
− | [1] Jeroen Fokker, The Systematic Construction of a One-combinator Basis for
| |
− | Lambda-Terms.
| |
− | Formal Aspects of Computing 4 (1992), pp. 776-780.
| |
− | http://www.cs.uu.nl/people/jeroen/article/combinat/combinat.ps
| |
− |
| |
− |
| |
− | * More examples
| |
− |
| |
− | The reification function takes, as one argument, an environment -- or
| |
− | the initial assumption. Oftentimes it is this:
| |
− |
| |
− | > gamma0 = G HNil 0
| |
− |
| |
− | Other environments may be used to reify *open* terms.
| |
− |
| |
− | We start with a few simple examples:
| |
− |
| |
− | > test1 = let term = \x y -> x in reify term gamma0
| |
− | > test2 = let term = \x y -> y in reify term gamma0
| |
− | > test3 = let term = \f x y -> f x in reify term gamma0
| |
− | >
| |
− | > -- \f x y -> f y x
| |
− | > test4 = reify (__::(t -> t1 -> t2) -> t1 -> t -> t2) gamma0
| |
− | >
| |
− | > -- \f g a b -> f (g a b)
| |
− | > test5 = let term = (.) (.) (.) in reify (undefined `asTypeOf` term) gamma0
| |
− | >
| |
− | > pz = (((. head . uncurry zip . splitAt 1 . repeat) . uncurry) .) . (.) . flip
| |
− | > test_pz = reify (undefined `asTypeOf` pz) gamma0
| |
− | >
| |
− | > -- \f g h x y -> f (g x) (h y)
| |
− | > test7 = let term = ((flip . ((.) .)) .) . (.) in reify term gamma0
| |
− |
| |
− | More complex are improper combinators:
| |
− |
| |
− | > -- \f -> f (\x -> x)
| |
− | > test6 = reify (__::((a->a)->b) -> b) gamma0
| |
− | >
| |
− | > -- X combinators
| |
− | > {-- commented out to speed up loading
| |
− | > test8 = let term = \a b c d -> c d (a (\x -> d))
| |
− | > in reify (undefined `asTypeOf` term) gamma0
| |
− | >
| |
− | > test9 = reify (undefined::(((a -> b -> c) -> (a -> b) -> a -> c) ->
| |
− | > (t3 -> t1 -> t2 -> t3) -> t) -> t) gamma0
| |
− | > --}
| |
− |
| |
− | Other terms to try:
| |
− | ((((.) . (.)) . (.)) . (.))
| |
− | ((flip .) . (((flip .) .) . (((.) . (.)) . (((.) . (.)) .))))
| |
− | reify const gamma0
| |
− | reify (.) gamma0
| |
− | reify (asTypeOf __ (.)) gamma0
| |
− |
| |
− | > term1B = reify (undefined:: t4 -> (t3 -> t4 -> t5 -> t1) -> t6 -> t3 ->
| |
− | > (t4 -> t -> t -> t5 -> t1 -> t2) -> t -> t5 -> t2) gamma0
| |
− |
| |
− | The type a->b however is not inhabitable: If we uncomment the following,
| |
− |
| |
− | > --test_not_derivable = reify (__::a -> b) gamma0
| |
− |
| |
− | we get the type error:
| |
− | No instances for (Resolve (Gamma (:*: (T2T (:*: a HNil)) HNil)) assum',
| |
− | HLookup rt HNil (:*: rt assum))
| |
− | arising from use of `reify' at ...
| |
− |
| |
− | That is, a |- rt is unprovable.
| |
− |
| |
− | * Code
| |
− |
| |
− | ** Preliminaries
| |
− |
| |
− | An abbreviated notation for undefined, which shall occur very often
| |
− |
| |
− | > __ = __
| |
− |
| |
− | Heterogeneous lists
| |
− |
| |
− | > data HTrue
| |
− | > data HFalse
| |
− | > instance Show HTrue where show _ = "HTrue"
| |
− | > instance Show HFalse where show _ = "HFalse"
| |
− |
| |
− | > data HNil = HNil deriving Show
| |
− | > data HCons a b = HCons a b deriving Show
| |
− |
| |
− | > -- Syntax sugar from the HList library. Thanks to Ralf Laemmel
| |
− | > infixr 2 :*:
| |
− | > infixr 2 .*.
| |
− | > type e :*: l = HCons e l
| |
− | > a .*. b = HCons a b
| |
− |
| |
− | Environment: hs are hypotheses: an HList of T2T, each of which states one
| |
− | assumption: an association of a type to a term
| |
− |
| |
− | > data Gamma hs = G hs Int deriving Show
| |
− | > newtype T2T t = T2T Term deriving Show
| |
− |
| |
− | Converting an implication to a type list of assumptions in inverse order
| |
− |
| |
− | > class I2L t tl | t -> tl where
| |
− | > i2l :: t -> tl
| |
− | > i2l = undefined -- it's a type-level function
| |
− | >
| |
− | > instance (IsFunction t flag, I2L' flag t tl)
| |
− | > => I2L t tl
| |
− | >
| |
− | > class I2L' flag t tl | flag t -> tl
| |
− | >
| |
− | > instance I2L' HFalse t (t :*: HNil)
| |
− | >
| |
− | > instance (I2L x tlx, I2L y tly, HAppend tly (tlx :*: HNil) tl)
| |
− | > => I2L' HTrue (x->y) tl
| |
− | >
| |
− | > ti1 = i2l (__::a->b->c)
| |
− | > ti2 = i2l (__::(a->b)->a->b)
| |
− | > ti3 = i2l (__::(a -> b -> c) -> (a -> b) -> a -> c)
| |
− |
| |
− |
| |
− | The main reification class
| |
− |
| |
− | > class Reify t gamma where
| |
− | > reify :: t -> gamma -> Term
| |
− | >
| |
− | > instance (IsFunction t flag, I2L' flag t (rt :*: at),
| |
− | > AddHyp gamma at gamma',
| |
− | > Resolve gamma' ((rt :*: HNil) :*: HNil))
| |
− | > => Reify t gamma where
| |
− | > reify t gamma = let (varlist, gamma') = add'hyp gamma (__::at) []
| |
− | > in foldr (\ (Var v) s -> L v s )
| |
− | > (resolve gamma' (((__::rt) .*. HNil) .*. HNil) [])
| |
− | > varlist
| |
− |
| |
− | Label top-level assumptions with variables
| |
− |
| |
− | > class AddHyp gamma tl gamma' | gamma tl -> gamma' where
| |
− | > add'hyp :: gamma -> tl -> [Term] -> ([Term],gamma')
| |
− | >
| |
− | > instance AddHyp gamma HNil gamma where
| |
− | > add'hyp g _ varlist = (varlist,g)
| |
− | >
| |
− | > instance AddHyp (Gamma ((T2T t) :*: hs)) r gamma
| |
− | > => AddHyp (Gamma hs) (t :*: r) gamma where
| |
− | > add'hyp (G hs varcount) _ varlist =
| |
− | > let v = (Var varcount)
| |
− | > hs' = ((T2T v)::T2T t) .*. hs
| |
− | > in add'hyp (G hs' (varcount + 1)) (__::r) (v:varlist)
| |
− |
| |
− |
| |
− | ** The SLD resolution algorithm
| |
− |
| |
− | > class Resolve gamma goals where
| |
− | > resolve :: gamma -> goals -> [Term] -> Term
| |
− | >
| |
− | > instance Resolve gamma HNil where
| |
− | > resolve _ _ pt = foldr1 (flip A) pt
| |
− | >
| |
− | > instance (HLookup g hs (g :*: assum),
| |
− | > HReverse assum assum',
| |
− | > Resolve (Gamma hs) assum',
| |
− | > Resolve (Gamma hs) gr)
| |
− | > => Resolve (Gamma hs) ((g :*: HNil) :*: gr) where
| |
− | > resolve gamma@(G hs _) _ pt =
| |
− | > let T2T t1 = hlookup (__::g) hs
| |
− | > in resolve gamma (__::gr) ((resolve gamma (__::assum') [t1]) : pt)
| |
− | >
| |
− | > instance (AddHyp (Gamma hs) (gc :*: gcr) gamma',
| |
− | > AddHyp (Gamma ((T2T gc) :*: hs)) gcr gamma',
| |
− | > Resolve gamma' ((g :*: HNil) :*: HNil),
| |
− | > Resolve (Gamma hs) gr)
| |
− | > => Resolve (Gamma hs) ((g :*: gc :*: gcr) :*: gr) where
| |
− | > resolve gamma@(G hs _) _ pt =
| |
− | > let t1 = let (varlist, gamma'::gamma') =
| |
− | > add'hyp gamma (__::(gc :*: gcr)) []
| |
− | > in foldr (\ (Var v) s -> L v s )
| |
− | > (resolve gamma' (((__::g) .*. HNil) .*. HNil) [])
| |
− | > varlist
| |
− | > in resolve gamma (__::gr) (t1 : pt)
| |
− |
| |
− | Lookup in the `associative' type-indexed list
| |
− |
| |
− | > class HLookup t l w | t l -> w where
| |
− | > hlookup :: t -> l -> T2T w
| |
− | >
| |
− | > instance (TypeEq t t' flag, HLookup' flag t ((T2T (t' :*: at)) :*: r) w)
| |
− | > => HLookup t ((T2T (t' :*: at)) :*: r) w where
| |
− | > hlookup = hlookup' (undefined::flag)
| |
− | >
| |
− | > class HLookup' flag t l rt | flag t l -> rt where
| |
− | > hlookup' :: flag -> t -> l -> T2T rt
| |
− | >
| |
− | > instance HLookup' HTrue t ((T2T (t :*: at)) :*: r) (t :*: at) where
| |
− | > hlookup' _ _ (HCons t _) = t
| |
− | >
| |
− | > instance HLookup t r w => HLookup' HFalse t ((T2T t') :*: r) w where
| |
− | > hlookup' _ t (HCons _ r) = hlookup t r
| |
− |
| |
− |
| |
− |
| |
− | > class HAppend l1 l2 l | l1 l2 -> l where
| |
− | > happend :: l1 -> l2 -> l
| |
− | > instance HAppend HNil l2 l2 where
| |
− | > happend _ l2 = l2
| |
− | > instance HAppend l1 l2 l => HAppend (a :*: l1) l2 (a :*: l) where
| |
− | > happend (HCons a l1) l2 = a .*. (happend l1 l2)
| |
− |
| |
− | > class HReverse l1 l2 | l1 -> l2
| |
− | > instance HReverse HNil HNil
| |
− | > instance (HReverse l1 l1', HAppend l1' (a :*: HNil) l2)
| |
− | > => HReverse (a :*: l1) l2
| |
− |
| |
− |
| |
− | ** Show Terms in a nice way
| |
− |
| |
− | > instance Show Term where
| |
− | > show (Var i) =
| |
− | > case divMod i 26 of
| |
− | > (0,i) -> simple_var i
| |
− | > (j,i) -> simple_var i ++ (show j)
| |
− | > where simple_var i = ["yphdcbaeijklmnofqrstuvwxgz" !! i]
| |
− | > show (A e v@(Var i)) = show e ++ " " ++ show v
| |
− | > show (A e1 e2) = show e1 ++ " " ++ "(" ++ show e2 ++ ")"
| |
− | > show (L i e) = show' [i] e
| |
− | > where show' vars (L j e) = show' (j:vars) e
| |
− | > show' vars e = "\\" ++ unwords (map (show.Var) $ reverse vars) ++
| |
− | > " -> " ++ show e
| |
− |
| |
− |
| |
− | * The second reifier: from types to bona fide Haskell terms
| |
− |
| |
− | The second reifier is a `lifted' version of the first one. Wherever we
| |
− | used regular Haskell lists before, we use HLists now.
| |
− |
| |
− | > newtype T2TT tl t = T2TT t deriving Show
| |
− | >
| |
− | > class RR t gamma where
| |
− | > rr :: t -> gamma -> t
| |
− | >
| |
− | > instance (IsFunction t flag, RR' flag t gamma)
| |
− | > => RR t gamma where
| |
− | > rr = rr' (__::flag)
| |
− | >
| |
− | > class RR' flag t gamma where
| |
− | > rr' :: flag -> t -> gamma -> t
| |
− | >
| |
− | > instance (IsFunction x flagx, I2L' flagx x tlx,
| |
− | > IsFunction y flagy, RR' flagy y ((T2TT tlx x) :*: gamma))
| |
− | > => RR' HTrue (x->y) gamma where
| |
− | > rr' _ _ gamma = \ (v::x) -> rr' (__::flagy) (__::y)
| |
− | > (((T2TT v)::T2TT tlx x) .*. gamma)
| |
− | >
| |
− | > instance (RResolve gamma ((t :*: HNil) :*: HNil) HNil t)
| |
− | > => RR' HFalse t gamma where
| |
− | > rr' _ _ gamma = rresolve gamma (((__::t) .*. HNil) .*. HNil) HNil
| |
− | >
| |
− | >
| |
− | > class RResolve gamma goals tl t | gamma goals tl -> t where
| |
− | > rresolve :: gamma -> goals -> tl -> t
| |
− | >
| |
− | > instance RResolve gamma HNil (t :*: HNil) t where
| |
− | > rresolve _ _ (HCons t HNil) = t -- foldr1 (flip A) pt
| |
− | >
| |
− | > instance RResolve gamma HNil (t1 :*: tr) (t->r)
| |
− | > => RResolve gamma HNil (t :*: t1 :*: tr) r where
| |
− | > rresolve g _ (HCons t r) = (rresolve g HNil r) t
| |
− | >
| |
− | > instance (RHLookup g gamma (T2TT (g :*: assum) g'),
| |
− | > HReverse assum assum',
| |
− | > RResolve gamma assum' (g' :*: HNil) ra,
| |
− | > RResolve gamma gr (ra :*: pt) t)
| |
− | > => RResolve gamma ((g :*: HNil) :*: gr) pt t where
| |
− | > rresolve gamma _ pt =
| |
− | > let T2TT t1 = rhlookup (__::g) gamma
| |
− | > ra :: ra = rresolve gamma (__::assum') (t1 .*. HNil)
| |
− | > in rresolve gamma (__::gr) (ra .*. pt)
| |
− | > -- the instance for improper combinators is left as an exercise to the reader
| |
− |
| |
− | > -- Lookup in the `associative' type-indexed list
| |
− | > class RHLookup t l w | t l -> w where
| |
− | > rhlookup :: t -> l -> w
| |
− | >
| |
− | > instance (TypeEq t t' flag,RHLookup' flag t ((T2TT (t' :*: at) tt') :*: r) w)
| |
− | > => RHLookup t ((T2TT (t' :*: at) tt') :*: r) w where
| |
− | > rhlookup = rhlookup' (__::flag)
| |
− | >
| |
− | > class RHLookup' flag t l w | flag t l -> w where
| |
− | > rhlookup' :: flag -> t -> l -> w
| |
− | >
| |
− | > instance RHLookup' HTrue t ((T2TT (t :*: at) tt) :*: r)
| |
− | > (T2TT (t :*: at) tt) where
| |
− | > rhlookup' _ _ (HCons t _) = t
| |
− | >
| |
− | > instance RHLookup t r w => RHLookup' HFalse t ((T2TT tl' t') :*: r) w where
| |
− | > rhlookup' _ t (HCons _ r) = rhlookup t r
| |
− |
| |
− |
| |
− | A few tests:
| |
− |
| |
− | > rtest1 = let f (x::a) (y::b) ::a = rr undefined HNil x y
| |
− | > in f 1 2
| |
− | > rtest2 = let f x y = rr (undefined::a->b->b) HNil x y
| |
− | > in f 1 2
| |
− | >
| |
− | > -- \f x y -> f x :: forall t t1 t2. (t -> t1) -> t -> t2 -> t1
| |
− | > rtest3 = let t f x y = rr (undefined::(t -> t1) -> t -> t2 -> t1) HNil f x y
| |
− | > in t Just 1 'c'
| |
− | >
| |
− | > rtest4 f g = rr (undefined::(b -> c) -> (a -> b) -> a -> c) HNil f g
| |
− | > -- *HC> rtest4 (:[]) (\x -> (True,x)) 10
| |
− | > -- [(True,10)]
| |
− | > -- must be truly polymorphic!
| |
− |
| |
− |
| |
− | * Equality and deconstruction of type schemas
| |
− |
| |
− | > class IsFunction a b | a -> b
| |
− | > instance TypeCast f HTrue => IsFunction (x->y) f
| |
− | > instance TypeCast f HFalse => IsFunction a f
| |
− | >
| |
− | > -- literally lifted from the HList library
| |
− | > class TypeCast a b | a -> b, b->a where typeCast :: a -> b
| |
− | > class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b
| |
− | > class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
| |
− | > instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast' () x
| |
− | > instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
| |
− | > instance TypeCast'' () a a where typeCast'' _ x = x
| |
− | >
| |
− | > class TypeEq' () x y b => TypeEq x y b | x y -> b
| |
− | > where type'eq :: x -> y -> b
| |
− | > type'eq _ _ = undefined::b
| |
− | > class TypeEq' q x y b | q x y -> b
| |
− | > class TypeEq'' q x y b | q x y -> b
| |
− | > instance TypeEq' () x y b => TypeEq x y b
| |
− | > instance TypeCast b HTrue => TypeEq' () x x b
| |
− | > instance TypeEq'' q x y b => TypeEq' q x y b
| |
− | > instance TypeEq'' () x y HFalse
| |
− |
| |
− | </pre>
| |
− |
| |
− | [[Category:Idioms]]
| |