-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closures, why do we need func and funcC variants? #341
Comments
This is a good issue. I hope you don't mind if I lay out my background thoughts. Should we distinguish function types? This awkwardness comes from the fact that closures and "contextless" functions (i.e.: C-style function pointer types) are distinguished. The reason that they're distinguished is that it's much cheaper to call a contextless function than a closure -- for a contextless function, you just jump to it, whereas for a closure you have to dereference the closure record to load the address of its supporting contextless function and pass it the environment captured in the closure. Most ML variants just lift all function types into closures (e.g. if you don't have context then just capture the unit environment). But then all function calls are as expensive as closure calls, and we've lost the ability to distinguish cases. If we accept that function types can be distinguished, can it be made less awkward to ignore their differences in cases where we don't care? At a basic level it should be, because our interpretation of type classes and overloading has no runtime cost (we just rewrite terms) then we should be able to leave the function kind abstract and let usage select an implementation for each kind it needs. It's a little awkward (you lose the immediate obvious structure of Maybe syntax can make this less awkward? It should be pretty easy to say that if you parse a type signature like OTOH, maybe you couldn't do that to the right of the Is it actually useful to overload the definition of a function in this way? Beyond distinguishing contextless functions and closures, there are reasonable interpretations of many other types as functions. An array can be considered as a (partial) function from index to value, a btree as a (partial) function from key to value, etc. And there are useful parametric definitions like that a pair of functions can be considered a function on pairs, or a sum of functions as a function on sums. APL programmers love this kind of overloading, and we can outdo them this way. :) Where else could this kind of thing be going on? This is actually similar to what we've seen with other types. For example, we have a baked-in variable-length array type But this isn't the only possible memory layout for an array. In our structured data files, we wind up storing sequences as "ropes" or linked lists of arrays. Although they don't have exactly the memory layout of the default array, they do have a length and can be indexed. So we wound up making an Here again we could have a front end rewriting of any function like Likewise for record types there's a similar overloading that we use (so that non-records can have synthetic properties), and variants. So that's established overloading for just about every kind of type. I think that it might be worth doing this kind of implicit abstraction in the front end, since it definitely saves you some awkwardness. At some level you'd probably want to disable it, at least e.g. where you're defining new instances of the |
Thanks. I wanted to make this exact sort of suggestion, but this clearly involves some significant changes. |
No, there's no overhead to using one function with a The only reason that the standard library here is in the state it's in is just history -- I didn't have all of the pieces in place when I needed some of these functions. Internally we've built up a different standard library, which I'm hoping to split out at some point but maybe it'd be easier to just update these bits here, or break it out entirely so it's easier for users to swap in their own standard library. re: filter, again just history (though Safe to say, it's worth at least a full pass through the standard lib here to freshen things up and reduce them where possible. Maybe worth also or optionally factoring it out so that it can be updated independently. |
I'm afraid find the way closures get created and treated by the type-system an annoying aspect of hobbes. From an immediate practical point of view, why are the std lib functions not written uniformly using apply? For instance if I rewrite filter as
Then filter seems to work with both functions and closures. Is there any downside to doing this?
The text was updated successfully, but these errors were encountered: