5 ms·
Eh, I think the OP had more of an argument. Saying something is a "non-argument" is not a useful rebuttal! I completely agree about the lack of names. I base
by chubot 9y ago
Eh, I think the OP had more of an argument. Saying something is a "non-argument" is not a useful rebuttal!
I completely agree about the lack of names. I based my current language project around Zephyr ASDL [1], a DSL used in Python to describe abstract syntax with the model of algebraic data types. And it does force you to use names, which I very much like.
I've looked at a lot of language implementations and algorithms in ML, and the first thing I read is the abstract syntax. And the lack of names always bugs me.
Also, the lack of namespacing. In Rust, the enum variants are scoped to the name of the sum type. But in ML you will often see people using explicit prefixes like in C, to provide your own namespacing. (At least, I saw this in the early implementations of the Rust compiler itself in OCaml.)
[1] http://www.oilshell.org/blog/tags.html?tag=ASDL#ASDL http://www.oilshell.org/blog/tags.html?tag=ASDL#ASDL
- mrkgnao 9y agoI agree with you on namespacing: I've found myself having to add prefixes myself when working with a bunch of similar datatypes with identically-named constructors (e.g. terms and types in a compiler have many similarities). Saying "non-argument" and ending it there is not useful, but in this case it's shorthand for something along the lines of "not even wrong", and I think that's a valid answer for some (not all!) of the points in the OP. To pick one example: the author gives an example of an eta-reduced function and then says: let fn1 a b c d = ... do something ... let fn2 a b c = fn1 a b c fn2 a b c d > and you're like ...wtf... where the hell does d come from. (Or their criticism of partially-applied functions, which is essentially the same thing.) This is a "non-argument": the only thing that is being made clear here is that the author hasn't spent sufficient time learning the idioms of an unfamiliar style of programming. Yes, horrible partial-application like f = (g .) . ($) . (. (. h)) or whatever is always a bad idea, but saying that writing map f is always worse than map (\x -> f x) is much more likely to be a symptom of unfamiliarity than a deep, cutting critique of functional programming style.
- lgas 9y agoThe whole thing was obviously driven by unfamiliarity and stubbornness in clinging to the familiar but your comparison of "map f" vs "map (\x -> f x)" is not equivalent to his comparison of "array.map(fn)" to "array.map(x => fn(x))". The haskell versions are equivalent but the javascript versions are not due to the airty issues raised in the comments.
- Drup 9y ago> Also, the lack of namespacing. In Rust, the enum variants are scoped to the name of the sum type. But in ML you will often see people using explicit prefixes like in C, to provide your own namespacing. (At least, I saw this in the early implementations of the Rust compiler itself in OCaml.) Well then that was badly written OCaml code. Instead of prefixing, you should use the (fantastic) OCaml module system. Also, OCaml has type-based disambiguation support for records and variants. Speaking of lack of names, OCaml supports "inline records" nowadays: type foo = | Bar of { this : int ; that : float } | Baz of baz
- mrkgnao 9y agoHow does record access work? Is it via generated record accessor functions (pretty crappy, used by Haskell) or does OCaml now have true row types (as in PureScript (and perhaps Elm))? Can you have multiple types with the same record field name?
- Drup 9y agoThe "inlined" record can only be accessed locally and can not be returned as a single value (See manual here: http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec271 http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec271). In general, multiple types can have the same record fields/constructor names. The typechecker uses type information to figure things out in case of conflicts. For example: type t1 = { foo : int ; bar : int } type t2 = { foo : string ; baz : int } let x = { foo = 2 ; bar = 2 } let y = { foo = "foo" ; baz = 3 } let f x = x.foo ^ "x" (* In case of doubt, uses the last one defined *) let g (x : t1) = x.foo + 1 (* Uses the types to find the right one *) let h { foo ; bar } = foo + bar (* Figure things out using the fields *) (Of course this is an extreme example, good code should be clearer than that :p) When a field "foo" comes from a type t in a module M (M.t), you can either use type disambiguation as above or qualify accesses : x.M.foo. All this also works with ADTs.
- KirinDave 9y agoThere is a deep desire in the OO world to make custom, domain specific ontologies for every domain. The FP approach attempts to do the opposite: create as few new names as possible (or when we do create them, they should not be exclusive unless they are truly domain specific). A modern way these meet in the middle is called "named row polymorphism" as demonstrated in PureScript. All three of these approaches have tradeoffs. It's not a very good idea to say any of them are uniquely suited even to a domain.
- jcelerier 9y ago> There is a deep desire in the OO world to make custom, domain specific ontologies for every domain. well, yes. A big selling point of OO was to allow big services company to have a crazy turnover, where everyone working on a codebase is fired and new hires can start being productive the next day on it. So everything has to be documented and named. Write the class you're assigned and don't do more ; you're just a microscopic cog in the machine.
- chubot 9y agoI think you're talking about a different issue regarding names (maybe because the original post got taken down.) I think he was just referring to lack of names in type declarations in OCaml. type expr = | Sum of (int, int) | Negation of int A sibling comment pointed out that this is now possible: type expr = | Sum of { left: expr ; right: expr } | Negation of { child: expr } The former style looks OK for this simple example, but I sympathize with the OP because "real" type declarations are more complicated, and that's where the problem with lack of names surfaces. You can imagine C WITHOUT names in structs, but WITH destructuring assignment of structs. That means you have to write the same names every time you destructure! That leads to inconsistent programs. ----- But I know what you are talking about with regard to names. I would say it is more of a Lisp approach than an FP approach in general. (There seems to be a resurgence of statically typed languages in FP in the last decade, so I distinguish them from Lisp. There is a good talk called "Observations of a Functional Programmer" which I link in this post [1], which talks about the schism.) Anyway, I have heard Rick Hickey (of Clojure) argue passionately against domain-specific ontologies, and I get his point. Another way I've heard it said is that "Java is like legos where none of the pieces fit together" (I think from Steve Yegge). Those criticisms are totally valid, but it's a different issue than the one OP was complaining about. If anything, OCaml has the same problems as Java. You need to "jump out of" the language to do metaprogramming, whereas in Lisp, metaprogramming is just programming. That is essentially what my blog post is pointing out. Thanks for the pointer on PureScript -- I will check that out. ------ Another thing I came across recently was libraries in statically typed FP languages to do simple visitor tasks, like: https://wiki.haskell.org/Scrap_your_boilerplate https://wiki.haskell.org/Scrap_your_boilerplate In Lisp this is not something "special". It doesn't require a named solution or "innovation". I don't use Lisp for other reasons, but I appreciate this aspect of it. [1] http://www.oilshell.org/blog/2016/12/05.html http://www.oilshell.org/blog/2016/12/05.html
- ernst_klim 9y ago>But in ML you will often see people using explicit prefixes like in C No, you would not, since OCaml/SML also has modules.