4 ms·
Bob Harper has also made this point before: “The power of type theory arises from its strictures, not its affordances, in direct opposition to the ever-popular
by catnaroek 11y ago
Bob Harper has also made this point before: “The power of type theory arises from its strictures, not its affordances, in direct opposition to the ever-popular language design principle “first-class x” for all imaginable values of x.” https://existentialtype.wordpress.com/2013/01/28/more-is-not-always-better/ https://existentialtype.wordpress.com/2013/01/28/more-is-not...
- colanderman 11y agoCompletely agree. People think I'm weird that I think functions being first-class is a dumb idea. But think about it: how often do you need to perform some complicated algorithm on a function, or store a dynamically generated function in a data structure? Almost always, first-class functions are simply used as a means of genericizing or parameterizing code. (e.g. as arguments to `map` or `fold`, or currying.) (Languages like Haskell and Coq that are deeply rooted in the lambda calculus are a notable exception to this; it's common to play interesting games with functions in these languages.) You can get the same capabilities by making functions second-class objects, with a suitable simple (non-Turing-complete) language with which to manipulate them. That language can even be a subset of the language for first-class objects: the programmer is none-the-wiser unless he/she tries to do something "odd" with a function outside the bounds of its restricted language. Generally, there is a clearer, more efficient way to express whatever it is they are trying to do. There is some precedent for this. In Haskell, typeclass parameters live alongside normal parameters, but aren't permitted to be used in normal value expressions. In OCaml, modules live a separate second-class life but can be manipulated in module expressions alongside normal code. In Coq, type expressions can be passed as arguments, but live at a different strata and have certain restrictions placed on them. Unfortunately designing languages like this is hard. It's easy to just say "well, functions are a kind of value in the interpreter I just wrote; let's make them a kind of value in the language". This is the thinking behind highly dynamic languages like JavaScript, Python, and Elixir: the language is modeled after what is easy to do in the interpreter without further restriction. The end result is a language that is difficult to optimize and analyze. It's a lot more work to plan out "well, I ought to stratify modules, types, functions, heterogenous compounds, homogeneous componds, and scalars, because it will permit optimizations someday". But these are the languages that move entire industries.
- theoh 11y agoIt's worth distinguishing between manipulating a function as an "external" value and making "internal" changes to a function. The first case is completely unproblematic. The second is potentially extremely complex and so far intractable in all but "duh" cases.
- colanderman 11y agoTo be clear, I'm talking about the former (manipulating it as an opaque value). I've never seen a program (save esoteric Haskell ones) that did anything with functions that even approached requiring Turing-completeness.
- codygman 11y agoHaskell isn't esoteric.
- colanderman 11y ago"Esoteric" applies to "programs", not "Haskell".
- deleted 11y ago[deleted]
- catnaroek 11y agoIn general, language design requires a balance between first-class vs. second-class objects. First-class objects are important because they let you write the program you want. Second-class objects are also important because they let you write programs that can be reasoned about. For instance, Haskell's type language (without compiler-specific extensions) has no beta reduction, because only type constructors can appear not fully applied in type expressions anyway. This reduces its “raw expressive power” (w.r.t. System F-ω, whose type level is the simply typed lambda calculus), but it makes inference decidable, which helps machines help humans. It also helps humans in a more direct fashion: Haskellers often infer a lot about their programs just from their type signatures, that is, without having to compute at all. It's no wonder that Haskellers love reasoning with types - it's literally less work than reasoning with values. So I agree with you that “first-class everything” isn't a good approach to take. A language with first-class everything is a semantic minefield: Nothing is guaranteed, sometimes not even the runtime system's stability. (I'm looking at you, Lisp!) --- But, on the specific topic of first-class functions, you'll pry them from my cold dead hands. Solving large problems by composing solutions to smaller, more tractable problems, is a style that's greatly facilitated by first-class functions, and I'm not ready to give it up.