Reputation: 6999
Let's say I have 100 functions of various simple signatures (start with monomorphic, but would like to make the polymorphic case work too) a -> b (Int -> Int, Int -> String, String -> Double, MyType -> MyOtherType, etc.)
Let's say I show a list of those to the user. The user selects one. I show a list of functions whose parameter is compatible with the selected function's output. The user selects one.
How could I now compose those two functions? The general case is a series of compositions, but this simple case I think covers the problem I'm working with.
I know I could try unsafeCoerce or Data.Dynamic, but I'm trying to see if I can avoid that, and those apparently are restricted to monomorphic types which is causing a problem.
I thought perhaps somehow I could create a data structure of all the functions and what they could be composed with, but I'm not sure about that. And when including polymorphic cases, it seems like it would be impossible.
Upvotes: 7
Views: 705
Reputation: 153172
The question boils down to this: How can I prove to the compiler that this input and that function and what I'm doing with that function's output all line up in a sensible way? There's a couple of answers.
unsafeCoerce
.Data.Dynamic
.a
and b
in your function's a -> b
type. Depending on just how many choices there are for a
and b
, this can be a bit of a drag.As an example of the third option, suppose you have f :: Int -> String
, g :: Double -> Bool
, choice1 :: Int
, choice2 :: Int
, choice3 :: Int
, and choice4 :: Double
. You could write something like this:
main = prompt "f or g" >>= \ans -> case ans of
"f" -> prompt "1, 2, or 3" >>= \ans -> case ans of
"1" -> doSomethingWithString (f choice1)
"2" -> doSomethingWithString (f choice2)
"3" -> doSomethingWithString (f choice3)
"g" -> prompt "4 is your only choice" >>= \ans -> case ans of
"4" -> doSomethingWithBool (g choice4)
This approach can often be cleaned up a lot -- for example, all of the "f" cases could be collapsed.
Upvotes: 12
Reputation: 12749
I'm not sure what you intend to do with the composed function once it's been created since its arguments and result will have unknown type. I guess you could just keep applying arguments to it until it is no longer a function, but you wouldn't be able to do anything useful with the result unless you constrained it to be a member of some class (like Show
).
Why do you want to avoid Data.Dynamic
? It seems like this is an inherently dynamic problem. In order to be able to match compatible functions, you will need a way to represent types as data, so Data.Typeable
seems appropriate.
This sounds like you want to do at runtime what the Haskell compiler does at compile time. Perhaps you could just dynamically construct the code and evaluate it on the fly with System.Eval.Haskell.
Upvotes: 1