
Hi all, I studied Haskell a few years ago at uni, but I haven't used it since, so I'm a bit rusty. But I've been inspired by this paper: http://citeseer.ist.psu.edu/585421.html which talks about building a combinator language for financial contracts, and which uses Haskell as the implementation language. ...anyway. Say I have a data type: data Thing = Thing { field_one :: String, field_two :: String, field_three :: Integer } I'd like to build a datatype that represents comparison rules on Things. A comparison rule would be something like "thing1.field_two == thing2.field_two" or "thing1.field_three > thing2.field_three". If I was writing python, I might do this as: class ThingCompare: def __init__(self, op, field, thing1, thing2): self.op = op self.field = field I could evaluate instances of ThingCompare as: def evalThingCompare(tc, thing1, thing2): return tc.op(getattr(thing1, tc.field), getattr(thing2, tc.field)) In Haskell, I envisage writing something like: data ThingCompare = TC Op Field So that if I had: eq x y = x == y then I could create a ThingCompare by saying: tc = eq field_two And I could evaluate my ThingCompares by defining: evaltc :: ThingCompare -> Thing -> Thing -> Bool evaltc (TC o f) t1 t2 = o (f t1) (f t2) But I can't figure out what types "Op" and "Field" should be. Any suggestions? Am I on the wrong track? (have I been corrupted by python's dynamic typing? :-) ) -- John.

John Fouhy wrote:
In Haskell, I envisage writing something like:
data ThingCompare = TC Op Field
This wants to be a bit more concrete: data ThingCompare = TC (a -> a -> Bool) (Thing -> a) so that you could then have something to execute your comparison thingy: runTC :: ThingCompare -> Thing -> Thing -> Bool runTC (TC compare extract) a b = compare (extract a) (extract b) and construct a value of it: tcEqFirst = TC (==) first_field so to compare two Things, you'd do something like this: runTC tcEqFirst a b

On 14/03/07, Bryan O'Sullivan
John Fouhy wrote:
In Haskell, I envisage writing something like:
data ThingCompare = TC Op Field
This wants to be a bit more concrete:
data ThingCompare = TC (a -> a -> Bool) (Thing -> a)
Hi Bryan, I actually had to write: data ThingCompare a = TC (a -> a -> Bool) (Thing -> a) runTC :: ThingCompare a -> Thing -> Thing -> Bool runTC (TC compare extract) x y = compare (extract x) (extract y) But thanks for your help; it set me back on the right track. I think things are slowly starting to come back to me :-) -- John.

On 14/03/07, Bryan O'Sullivan
This wants to be a bit more concrete:
data ThingCompare = TC (a -> a -> Bool) (Thing -> a)
so that you could then have something to execute your comparison thingy:
runTC :: ThingCompare -> Thing -> Thing -> Bool runTC (TC compare extract) a b = compare (extract a) (extract b)
...ok, I'm stuck again. I'm still paralleling "How to write a financial contract", so I'd now like to combine ThingCompare. This is my code: ----------- data Thing = Thing { field_one :: String, field_two :: String, field_three :: Integer } t1 :: Thing t1 = Thing { field_one = "foo", field_two = "bar", field_three = 17 } t2 :: Thing t2 = Thing { field_one = "foo", field_two = "baz", field_three = 13 } type BooleanOp a = a -> a -> Bool type Field a = Thing -> a data ThingCompare a = TC (BooleanOp a) (Field a) | And (ThingCompare a) (ThingCompare a) | Or (ThingCompare a) (ThingCompare a) and :: ThingCompare a -> ThingCompare a -> ThingCompare a and tc1 tc2 = And tc1 tc2 or :: ThingCompare a -> ThingCompare a -> ThingCompare a or tc1 tc2 = Or tc1 tc2 runTC :: ThingCompare a -> Thing -> Thing -> Bool runTC (TC compare extract) x y = compare (extract x) (extract y) runTC (And tc1 tc2) x y = (runTC tc1 x y) && (runTC tc2 x y) runTC (Or tc1 tc2) x y = (runTC tc1 x y) || (runTC tc2 x y) tcEqOne = TC (==) field_one tcEqTwo = TC (==) field_two tcGtThree = TC (>) field_three ---------- So I can happily write, for example, tcEqBoth = and tcEqOne tcEqTwo runTC tcEqBoth t1 t2 and hugs will tell me "False". But if I want to combine tcEqOne and tcGtThree I run into type problems, because one of them uses Strings and the other Integers. I want to break the type dependence between the arguments of "And"; can I do this? If I type: data ThingCompare a = TC (BooleanOp a) (Field a) | And (ThingCompare b) (ThingCompare c) | Or (ThingCompare b) (ThingCompare c) hugs complains that it doesn't know what "b" and "c" are. -- John.

John Fouhy:
But if I want to combine tcEqOne and tcGtThree I run into type problems, because one of them uses Strings and the other Integers. I want to break the type dependence between the arguments of "And"; can I do this?
Try this. You'll also need to change "ThingCompare a" to "ThingCompare" in all your function type signatures.
data ThingCompare = forall a. TC (BooleanOp a) (Field a) | And ThingCompare ThingCompare | Or ThingCompare ThingCompare
You might also like to read my post from a few minutes ago: http://www.haskell.org/pipermail/haskell-cafe/2007-March/023465.html

I said:
Try this. You'll also need to change "ThingCompare a" to "ThingCompare" in all your function type signatures.
data ThingCompare = forall a. TC (BooleanOp a) (Field a) | And ThingCompare ThingCompare | Or ThingCompare ThingCompare
Sorry. For that to work, you would need "-fglasgow-exts", which presumes you are using GHC. I just noticed from your post that you are using Hugs.

John Fouhy wrote:
But if I want to combine tcEqOne and tcGtThree I run into type problems, because one of them uses Strings and the other Integers.
Yep. The observation you can make here is that TC is really a fancy way to write a function that takes some parameters and returns a Bool. So And and Or don't really care what types their parameters have, so long as they return Bools when evaluated. If you could get rid of the type variable on the left, you'd be set, because then And and Or would each take ThingCompares of any type. And you can! See section 7.3.4 of the Hugs manual for "existential types": http://cvs.haskell.org/Hugs/pages/hugsman/exts.html#sect7.3.4 This will let you move your type variable a into the TC branch alone as "forall a. TC ...", so it will not, er, infect the rest of the branches of the type. Then And and Or will neither care, nor be able to find out, about what TC really has inside, which is as you might wish.

John Fouhy wrote:
[...] data Thing = Thing { field_one :: String, field_two :: String, field_three :: Integer }
type BooleanOp a = a -> a -> Bool type Field a = Thing -> a
data ThingCompare a = TC (BooleanOp a) (Field a) | And (ThingCompare a) (ThingCompare a) | Or (ThingCompare a) (ThingCompare a) [...] tcEqOne = TC (==) field_one tcEqTwo = TC (==) field_two tcGtThree = TC (>) field_three
I'm not quite sure what you want to do with explicitly represented comparisons. Maybe optimize them afterwards? Otherwise, BoolOp is likely to be your best comparison representation. You can separate the Field and the BoolOp parts with a small combinator by :: BoolOp a -> (b -> a) -> BoolOp b by f g x y = f (g x) (g y) Then, the three comparisons become tcEqOne = (==) `by` field_one tcEqTwo = (==) `by` field_two tcGtThree = (>) `by` field_three Appealing to the famous instance Monad ((->) a), you can also say and, or :: BoolOp a -> BoolOp a -> BoolOp a and = liftM2 $ liftM2 (&&) or = liftM2 $ liftM2 (||) Regards, apfelmus
participants (4)
-
apfelmus@quantentunnel.de
-
Bryan O'Sullivan
-
John Fouhy
-
Matthew Brecknell