
On 2010 Oct 14, at 09:19, Evan Laforge wrote:
How are you expecting to call the functions in that container? "for f in c: try: return f(*misc_args) except: pass"?
to_do = [(call, (AuntMabel,)), (buy, ([(12*kg, sugar), (6*bushel, wheat)])), (introduce, (Romeo, Juliet))]
for do,it in to_do: do(*it)
As has been pointed out, simply write it like this:
to_do = [call AuntMabel, buy [(12kg, sugar), (6 bushel, weat)], etc.]
Which works for this case, but not in general. For example here's the memoizer example I used in response to Brandon: def memoize(fn): cache = {} def memoized_fn(*args): if args not in cache: cache[args] = fn(*args) return cache[args] return memoized_fn You memoize a function once, but it will be given different arguments, many times, at a later time. But what should the type of fn be? What should the type of args be? In Python, I don't care, as long no type error occurs when they are combined thus: fn(*args) How do you let Haskell type check the combination of the types, rather than the individual types? My answer seems to be: define a variant type for holding the combinations. The problem with this is that the set of allowed combinations is closed at library compile time. I want it to remain open for extension. In Duck Typing this happens trivially.
If they are monadic actions, you can call 'sequence_' on them when you want them to "happen". If not, you really just have a list.
The thing is, I can arrange for them to be compatible. Python won't be able to confirm this statically, but is it too much to ask of Haskell to have it figure out (statically) that all of
(Int -> Bool, Int) (Banana -> Apple -> Orange -> Kiwi -> Bool, (Banana, Apple, Orange, Kiwi)) (Bool -> Bool -> Bool, (Bool, Bool))
can be combined to give Bool ?
Yes, this sounds like an existential:
data Boolable forall a. = Boolable (a -> Bool)
But despite the fact that I've been keeping them in the back of my mind for years, I've never once come up with a place where one would actually be useful. I guess I just don't think that way.
I think that Haskell allows so many completely different approaches to things, that serious Haskell programmers are essentially using completely different languages which share a small common core :-)
I agree with you that this is sometimes easier in a dynamic language because you can reuse the implementation language at runtime.
I don't think I'm looking for that in this case. I'm just asking to be allowed to stick both
(A -> B -> X, (A, B))
and
(C -> D -> E -> X, (C, D, E))
etc. in the same container, because, frankly, in the context in which they are used, they *are* the same.
Maybe you should focus less on the particular implementation you want and more on the end result? If you start off saying "I want heterogenous lists" then you'll start off with a problem for haskell :)
Of course. Please don't get the impression that I'm trying to fit things into my box and won't accept anything else. I'm here to learn. In the process of explaining what I mean in some particular case, I end up using language from which says that "I want this", but that only refers to the exploration of one particular approach. I am open to, and eagerly encourage, completely different suggestions.
Haskell doesn't have the interpreter around at runtime. But if you know exactly what parts of the interpreter you want, you can recover them, i.e. with Dynamic or by using 'hint' in the extreme.
Hint. Hmm. Embedding an interpreter into your code. I can imagine lots of interesting uses for this. But I don't think I want/need it in this case. Thanks for pointing it out, though.
apply1 f [x] = f x apply1 _ _ = throw hissy fit apply2 f [x, y] = f x y etc.
I would hope that the types could be checked statically, as I explained above.
They can. The strings coming in from the user, of course they can't,
Sure, but that's why we have a ParseError constructor in our Question type.
because they're not even known statically. The 'apply1' function, of course, is statically checked in that 'f' is required to be a function with a single string argument. Well, of the same type as the list passed.
But I feel rather cramped by x and y in apply2 being constrained to having the same type.
I'm pretty sure that you could never come up with a sufficiently large set of primitives. Even if you could, it seems like far too much work, given that the ability to store arbitrary (sets of co-operating) functions (which, together, always return the same types) in the same container, trivially provides you with full generality.
Could you provide a more concrete example? So far the simple example of int accepting functions with different arities is pretty easy to implement with a plain list,
Trivial, as long as you combine the components immediately. If you need to hold the components separately it becomes trickier. Specifically, you need to create a variadic wrapper for holding the components, at which point you lose extensibility. Again, I'm sure this isn't the only way, but it's the one that my inexperienced mind sees immediately.
so maybe you could provide a bit of python or something that does what you want and would be harder with static types?
Is the memoizer show above sufficient? If not I'll try to distil a minimal set of conflicting question types in the maths test example.
Eventually, some invisible line is crossed and you have an EDSL for writing math tests.
That is *exactly* where I am heading with this.
Well, good, haskell's supposed to be good at EDSLs :)
Your Question type could look like 'String -> Answer' and Answer = 'Wrong String | Right | ParseError String'.
Actually, I think it should be more like:
Answer = ParseError String | Wrong String | NeitherWrongNorRightSoTryAgain String | Right
Well sure, the point is that neither of these types are even polymorphic, let alone existential.
Yes, it's completely irrelevant to the meat of the discussion.