-
Notifications
You must be signed in to change notification settings - Fork 10
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
functor syntax #8
Comments
With regards to dropping One place where opaque ascription is useful is for a compile-time selection from among a number of modules matching the same (large) signature. For example, consider implementing the "default"
and know that all clients cannot exploit the equality of Yes, I could accomplish this with something like:
but I would actually need to lift every operation one by one. Another, less compelling, example is with signatures that implement view-like abstractions:
For many trees, the
rather than
and need to write all of the |
I would argue in favor of getting rid of translucent ascription and just make " I would also advocate getting rid of " |
I wholeheartedly agree. We often don’t want concrete equality to be exported, as in: signature IS = sig structure IS :> IS = struct val empty = [] fun sing x = [x] fun union(xs, ys) = xs @ ys fun insert(x, nil) = [x] fun rep nil = nil val xs = IS.union(IS.union(IS.sing 3, IS.sing 4), IS.sing 3); In teaching, I find opaque ascription much easier to explain and motivate: “just look at the signature, that’s exactly what you’ll get”. |
Doing so causes you to need way too many sharing specifications or where type’s. I changed my mind on this issue exactly because of teaching (and practicality).
|
It may depend on programming style. I have not found the where types to be a particular burden in my code or my teaching, but I may not be as strict about using " |
part of it is that we limit ourselves to where type for compatibility, but more generally it is convenient to have sharing by construction as well as by specification. it’s just too finicky to demand that everything be an abstract type when in fact what you want is a type class. transparent ascriptions implement type classes; opaque ascriptions implement abstract types; you want both. because datatype’s are abstract, i can get away with only type classes, and that is easy to teach. matthew’s remarks are well-taken, so maybe we need to retain opaque ascription, but it would be a disaster to get rid of type classes (rather, to require them to be simulated using abstract types and where type by hand). bob
|
Hi Bob, Perhaps I’m being dense, but it didn’t seem like you addressed my point (and example) about how datatypes aren’t abstract enough — often we don’t want to export any kind of equality, or the equality we want isn’t concrete equality. I can see the argument for having both kinds of ascription (even if I almost never use transparent ascription), but can’t see how doing away with opaque ascription can be fully compensated for. Alley
|
My mistake. Yes, you’re right. I wasn’t thinking of it because when we teach ML to freshmen we PROHIBIT any use of equality testing, and any use of Booleans, so equality never comes up at all. The whole business about equality in SML is a mess; it should be replaced by uses of modular type classes. Bob
|
I also think that Let me explain. In practice -- especially in signatures used with Take the unavoidable Map functor as an example:
Here, the type
And even that is yet too verbose, I'd want to be able to shorten the clumsy Under such an approach, no normal use of |
In any form of dependent typing a family is a fibration. You are just talking about another syntax for the same thing. The psychology of what it means to be a component is not part of the concept. Any component can function, a posteriori, as an argument, and any argument is implicitly a component. It's very simple and flexible. Bob
|
Sure, but as always, syntax matters, and it's the very subject of this issue :). The abstract conceptual equivalence simply isn't accessible in the concrete language right now. |
But it’s a positive disadvantage to commit in advance to what is a “paremeter” and what is a “result”. The whole beauty of the pullback notation is that it avoids pre-commitment. I think it’s a matter of psychology, really. People are trained to expect some sort of function-style parameterization mechanism. That’s not what we have. So what? They also expect “methods” and “classes” and “instances” and whatnot. We don’t give them that either. Bob
|
I am with you regarding the theoretical beauty, but in the vast majority of practical cases, the advantage is purely hypothetical, and diminished by the disadvantage of a cumbersome notation. Adequate sugar would be a remedy.
Well, in a language where every other abstraction mechanism is via function-style parameterisation they are kind of right to expect this, wouldn’t you agree? Either way, we can’t deny that it increases the barrier to entry. The argument for more conceptual generality would be more compelling if we applied it to other cases as well. The irony is that if you carry that through (like MixML did, for example), then you end up with something that is much closer to an object-oriented language. And indeed, the mechanism fits the expectations from that world more naturally, where it is close to the notion of refinement through mixin composition. |
yep, except that dave's formulation predated that stuff by decades. i would say that his setup inspired the (bad imo) mixin ideas.
|
I like the parameterized signatures thing due to the UX principle of affordances and constraints. But it seems like @rossberg wants the shorter syntax, while @RobertHarper doesn't want to restrict the functionality of signatures. So why not just make parentheses syntactic sugar for signature MAP = sig
type key
(* ... *)
end
functor Map(Key : ORD) :> Map(type key = Key.t) = (* ... *) I don't really know Rust, so this stuff might be wrong [click to expand].FWIW, Rust uses the syntax
I think you could do something like: sig
type k
include MAP(type key = k)
end In order to recover the "result" interpretation.
I'm not a fan of transparent ascription, but one elegant thing about functor Map (Key : ORD) = (* Map body *)
structure IntMap = Map (IntOrd) The structure IntMap = let
structure Key : ORD = IntOrd (* transparent ascription *)
in (* Map body *)
end Since |
Personally I find the syntax for functors disheartening, especially when "sharing" and "where" are used. I never know how to indent, and I never find a way to make it look nice.
I think there is general agreement that the parameter of a functor should be a specification, so that it's a kind of keyword style. SML97 admits that and a positional style, but I think we should just delete the latter entirely. Yes, it rules out F(G(X)), but how often does that come up in practice?
When the realization syntax is so heavy ("where type" instead of "where", and perhaps with "and"), it's all-but-impossible to write things neatly. Lightening the syntax would help, but not solve the problem. Maybe it's not solvable, but it sure would be nice to figure out something cleaner in the common case.
A related point is that I was once an advocate for the :> ascription (well, not that notation, but that concept), but in teaching and in practice I find it to be far more trouble than it's worth. It's easier to confine abstraction to the data types, and use that. (We could even adopt the "newtype" syntax from Haskell for the single-constructor case, but I'm not sure it's a good idea.) Are there any remaining advocates for :>?
The text was updated successfully, but these errors were encountered: