1. 4

    Went to the National Gallery of Victoria (Melbourne, Australia), and saw some works of art that are on loan from MoMA in New York. Was very inspiring!

    Gonna do some more messing around on Pikelet. Trying to look at implicit arguments, and rework how I do variable binding. As you may notice I created an organisation for it last week!

    1. 4

      Moved my language project (dependent typed systems programming language) to a new Github organisation: https://github.com/pikelet-lang - been working up to a 0.1 release, which will sadly just be an interpreter. I am definitely aiming for unboxed data types and closures, linear types, compile time evaluation, and low level interop in order to win the right for the ‘systems language’ moniker though.

      1. 19

        While I do find Pony interesting from a technical perspective, I’ll admit that publishing every point release changelog to lobsters is an extremely generous understanding of what some of us would like the “release” tag to showcase.

        Major features? Sure. Security fixes, yes. But:

        Pony 0.23.0 is primarily a bug fix release but does also include some additional changes. Upgrading is recommended. There is a single breaking change caused by the implementation of RFC 56.

        is arguably less interesting.

        1. 4

          Yeah, I’d be fine with a post when notable, interesting things have been added/removed/changed. Pony is an interesting language! But yeah, posting this one seems like it is wasting a bit of goodwill.

          1. 2

            as I write this, this story is hidden by 8, +10, -5 spam, which indicates to me that might be better to refrain unless there is a major milestone, or an interesting new feature that might spur some discussion.

            Adding some additional commentary about why the breaking type change is interesting or useful might help; there are enough language implementation nerds who read lobsters regularly that it could generate some discussion, especially since the capability types are one of pony’s most compelling and novel features.

            1. 4

              I wont be posting releases to lobsters anymore.

              1. 3

                I don’t think anyone wants you to stop posting releases. They are just expecting more from each post.

                1. 1

                  Perhaps. Either way. Not submitting them anymore.

          1. 1

            Does anyone know of a good introduction to property based testing?

            1. 1

              At what level? Lectures by the quickcheck guy are usually a good basic introduction.

              1. 1

                http://propertesting.com/ is a nice one, targeted at Erlang.

                1. 1

                  This blog for the hypothesis Python library has a lot of great articles about how to use this stuff in “enterprise-y” software.

                  To be honest it was way more convincing to me than most other articles as to the utility of this stuff for higher-level applications

                1. 4

                  Can anyone help me understand why Metal was designed? Apple’s a heavy hitter in Khronos, right? So what was it that they felt like they couldn’t accomplish with OGL/OCL? Are there non-Mac targets that support Metal?

                  1. 6

                    OpenGL is a tired old API that is too high level for high performance graphics work. At the time when Metal was being developed folks were working on lower level APIs to expose the GPU more, like Mantle and DirectX 12, and Metal was Apple’s offering. I believe Mantle eventually evolved into Vulkan, but for some reason Apple is continuing to promote Metal. It’s a nicer API for Swift users, but that’s about it. I would have preferred that they’d make a safe API over Vulkan for Swift like Vulkano, they seem to be under some weird impression that they’ll be able to trap devs in their platform with their own, proprietary API. Or maybe they just can’t bear to give up all the sunk cost.

                    1. 2

                      they seem to be under some weird impression that they’ll be able to trap devs in their platform with their own, proprietary API

                      Is it not working quite well for Microsoft with DirectX?

                    2. 1

                      As I vaguely recall, it started on ios as a way to utilize their graphics chips faster and more efficiently (lower overhead).

                    1. 5

                      Thankfully there’s MoltenVK, an implementation of the Vulkan API on top of Metal. Khronos has thankfully worked to get it open sourced for us! I’ve not used it before, so can’t claim it works as advertised, but if I was doing graphics work that’s where I’d head these days. Very sad that Apple continues to dig in its heels on this.

                      1. 3

                        Thought this was really neat. I’ve often thought that formal methods would be super handy on the front end, and likewise, I’ve also thought that formal methods folks could also learn a ton from UI folks. Great to see some work done towards bridging the gap!

                        1. 3

                          One of the killer features of the Alloy specification language is that Daniel Jackson deeply cares about UIs. It makes using the IDE way more pleasant than any comparable FM.

                          1. 1

                            I previously submitted this work that applied formal methods to GUI. It’s more complex but integrates with Spin. The visual formalism in this article reminded me of what Tersus looked like years ago when I looked at it. I’m not sure what it looks like now.

                          1. 2

                            The whole thing is great, but one idea that seems particularly useful in arbitrary languages without regard to how it fits with other features is to specify the list of globals used by a function. In Python-like syntax, imagine this:

                            def draw_quad(origin, left, up) [m]:
                                m.Position3fv(origin - left - up)
                                m.Position3fv(origin + left - up)
                                m.Position3fv(origin + left + up)
                                m.Position3fv(origin - left + up)
                            

                            Now you get a guarantee that the function uses zero globals besides m.

                            1. 4

                              In PHP you have something like that, global variables are not accesible from inside functions unless you specifically allow each one you want

                              $m = new M();
                              function draw_quad($orgin, $left, $up){
                                  global $m; // or $m = $_GLOBALS['m'];
                                  $m->Position3fv($origin - $left -$up);
                                  $m->Position3fv($origin + $left - $up);
                                  $m->Position3fv($origin + $left + $up);
                                  $m->Position3fv($origin - $left + $up);
                              

                              in practice, I haven’t found useful global variables other then the contextual ones ($_POST. $_GET, $_SESSION), which are superglobal and always defined

                              1. 2

                                I’d like to see something similar but generalised to “contextual” environmental bindings, rather than traditional global vars. And a compiler that ensures that somewhere in all call chains the binding exists. But you might want a global in some cases, or a “threadlocal”, or an “import” of some sort, or something like the context in react, etc.

                                Some mechanism in which the compiler makes sure the environmental dependencies are fulfilled, without necessarily requiring that value be propagated explicitly through each owner/container between provider and consumer.

                                1. 4

                                  I can’t find it but Scala has an extra context var that is passed with function invocation.

                                  And early Lisps had dynamic scope, meaning that a var bound to the next occurrence up the call stack.

                                  Both of these mechanisms supply the building blocks for AoP, so that a programmer can mixin orthogonal properties.

                                  1. 3

                                    And early Lisps had dynamic scope, meaning that a var bound to the next occurrence up the call stack.

                                    Today they still have it - see DEFVAR and DEFPARAMETER in Common Lisp.

                                    1. 2

                                      I can’t find it but Scala has an extra context var that is passed with function invocation.

                                      In Scala you can use implicit parameters for this:

                                      def foo(origin: Vec3, left: Vec3, up: Vec3)(implicit m: Context) {
                                          m.Position3fv(origin - left - up)
                                          m.Position3fv(origin + left - up)
                                          m.Position3fv(origin + left + up)
                                          m.Position3fv(origin - left + up)
                                      }
                                      

                                      In Haskell you could use a reader/writer/state monad thingy. In Koka or Eff you could use effects.

                                      1. 2

                                        Yeah, Scala’s context is probably closest to what I’m thinking of, from what I know of it.

                                      2. 4

                                        You can kinda get this with effect types. Effect types let you label certain functions as using resource A or B, and then you can have a documentation mechanism for what dependencies are used, without passing stuff around.

                                        It can still get a bit heavy (at least it is in Purescript), but less so than dependency injection

                                      3. 1

                                        A compiler or analyzer should be able to tell you that just from what variables or expressions go into the function. A previously-declared global would be one of the arguments. Why do we need to declare it again in the function definition?

                                        1. 1

                                          See my final sentence. “Now you get a guarantee that the function uses zero globals besides m.” The program documents/communicates what it uses. The compiler ensures the documentation is always up to date.

                                          In my example, m is not one of the arguments. Because m is a global, lexically accessible anyway in the body of the function. There’s no redundancy here.

                                          1. 0

                                            I’m saying a compiler pass should be able to do the same thing without a language feature. I think it won’t be important to that many people. So, it will probably be optional. If optional, better as a compiler pass or static analysis than a language feature. It might also be an easy analysis.

                                            1. 3

                                              You’re missing the point. It’s a list of variables that the code can access anyway. What would a compiler gain by analyzing what globals the function accesses?

                                              There are many language features that help the compiler do its job. This isn’t one of them. The whole point is documentation.

                                              (Yes, you could support disabling the feature, using say syntax like [*]. C++ has this. Just bear in mind that compilers also don’t require code comments, and yet comments are often a good idea. SImilarly, even languages with type inference encourage documenting types in function headers.)

                                        2. 1

                                          What if the Position3fv method uses global variable n? You also need to specify that for draw_quad. This quickly blows up like checked exceptions in Java and people want shortcuts.

                                          1. 2

                                            The problem with checked exceptions is that they discourage people from using exceptions. But there’s no problem in discouraging people from using global variables.

                                            I don’t mean to imply that I want all code to state what it needs all the time. In general I’m a pretty liberal programmer. It just seems like a good idea to give people the option to create “checkable documentation” about what globals a piece of code requires.

                                        1. 6

                                          Sometimes I wonder why recent languages don’t support multiple [return] values. We are even using destructuring assignment as a poor man’s multiple values. It solves the problem described in the article w/o cluttering the functions code with conditionals

                                          1. 2

                                            Most recent languages honestly do (Python, Elixir, Go, Rust, Kotlin, and Swift (I think) to name a few), and they’re getting backported in some others (C# 7 and C++17, for example) by improving tuples.

                                            I think the bigger issue with most of these languages is they don’t have anything like ML/Rust/Erlang-style fail-if-the-return-looks-like-this mode that lets you effectively use them as if they were exceptions when you want—e.g., the frequent pattern in Erlang/Elixir of Foo, ok = some_fun(), or Rust’s let v = bar()?;. Without that, you either do what Ruby’s doing here (throw an exception if exception: true is passed), or you have to explicitly check yourself (where Go is the extreme example of if foo, err := bar(); err != nil { ... } being a constant refrain).

                                            1. 1

                                              I don’t now about Elixir, Go or Kotlin, but neither Python or Go support multiple-values. In Go you have to accept to assign all the values the function returns, which kinda defeats the whole purpose of having multiple return values in the first place! (I checked in a REPL just to be sure). I haven’t kept up with recent developments in Python3 but AFAIK Pythonistas use lists or dictionaries to mimic multiple-values. Similarly in ES6 people use dictionaries + destructuring assignment in ES6. For Example

                                              const foo = () => ({a: 1, b: 2})
                                              
                                              const {a} = foo()
                                              
                                              console.log(a)
                                              

                                              Prints 1. The ES6 approach is better than Golangs and Python solution but it still forces the caller to reuse the name the callee decided on[0] plus all the return values are allocated on the stack.

                                              AFAICT Rust again doesn’t support multiple value but uses pattern matching (which destructuring assignment is one example of) to mimic them.

                                              Multiple values are useful for more than exception handling btw, one example is the division operator

                                              [0]: Yeah I know one can rebind the name, but the syntax is cumbersome to use and one still have to remember the name of the value as opposed to its purpose, which is easier to remember).

                                              1. 2

                                                Well, now I’m confused. On the one hand, you say,

                                                …neither Python [n]or Go support multiple-values

                                                …and then immediately link to a REPL that starts with “Go supports multiple return values,” and even shows an example of ignoring some of the returned values. Likewise, while I agree with you that Python uses sequences for multiple returns behind-the-scenes, the use in practice looks like

                                                def foo(a, b): return b, a
                                                c, d = foo(1, 2)
                                                

                                                which is indistinguishable in practice from what I do in an ML. (And in Python 3.5 and later, you can even do things like a, b, *rest = foo().) Python even has multiple-return division operations (see math.fmod from as far back as Python 2.7).

                                                Could you give an example of a language that does what you want, and how it differs from the previous? I feel as if we’re using the same words, but for radically different things.

                                                1. 2

                                                  I’m sorry I wasn’t more clear in my previous reply.

                                                  Could you give an example of a language that does what you want, and how it differs from the previous?

                                                  Common Lisp.

                                                  and how it differs from the previous?

                                                  • The caller doesn’t have to be aware that the callee supports multiple values unless they want to use it.
                                                  • The compiler can determine at the call site how many values are going to be used and allocate appropriately.

                                                  which is indistinguishable in practice from what I do in an ML.

                                                  Yes, and to the best of my knowledge MLs don’t support multiple values. Pattern matching enables one to mimic multiple values, which is what most recent language are doing and it provides at least 90% of the value of having multiple values.

                                                  The Go REPL example shows that the caller must be aware of the amount of values the callee is returning, which leads to clumsy UX. I’m guessing you wanted to link to modf but I’m not sure what you were getting at. The CL example of / shows how we can re use the same function instead of having mod and div. Similarly be taken advantage retrieving the value in a map where nil indicates absence without affecting the UX of the ‘happy path’ (ie. nil is not possible a value in the map).

                                                  –––––––

                                                  It is kinda ironic that my comment was motivated out of the idea of less is more in language design, in light of all the feature creep I’m seeing recently in JS and Ruby, but it appears that the approach of recent languages is better example of less is more ¯_(ツ)_/¯

                                                  1. 1

                                                    to the best of my knowledge MLs don’t support multiple values

                                                    Returning multiple values (like in Go) is just a special case of tuples, which most MLs support. So you don’t need an extra language feature for this. But I wouldn’t use this for errors like Go does - it makes more sense to use a variant/sum type for this.

                                          1. 3

                                            I am trying to solve a similar/related problem in a personal project of my own that specifies a grammar and language for generating configurations. It’s very much a work in progress but has what looks like some parallels to this languages goals.

                                            My attempts that are in no way ready for anyone to use for serious things is at: https://crates.io/crates/ucg and documented at: https://docs.rs/ucg/0.1.0/ucglib/

                                            1. 4

                                              I’d also recommend checking out Dhall: https://github.com/dhall-lang/

                                              1. 1

                                                I’ve seen dhall and liked some of what it was doing. It’s good to see movement in this space. We don’t really need more configuration languages though. What we need are good ways to manage all the different types of configuration languages we currently have that isn’t a template language.

                                            1. 5

                                              Finally implemented dependent record types in Pikelet! Now working on adding some simple support for pattern matching.

                                              1. 3

                                                As I see it, despite the wave of blog posts, Rust is still niche. Its usage is very small compared to the massively popular languages. I predict that unless Rust changes a lot and makes it easy for anyone to use, its usage will remain comparable to Haskell or Lisp usage: widely loved or desired language but too weird for mass adoption.

                                                Let’s see in five years how this plays out. Maybe I’ll be wrong.

                                                1. 11

                                                  I don’t disagree and that makes me sad for what we as programmers ask for from our tools. I went through something like this a long time ago. I chose the “easy route” over the “difficult” one. I wish I hadn’t.

                                                  In my case, I went with C over Ada because Ada frustrated me and it was hard to get it to compile. That my C programs were easy to compile but crashed all the time on me was never something that I saw as problematic until I gained a lot more experience.

                                                  I wrote a bit about that a couple years back.

                                                  https://www.monkeysnatchbanana.com/2016/02/22/learning-from-ada/

                                                  1. 3

                                                    Good write-up. That should be a submission instead. :)

                                                    1. 4

                                                      Thanks. Already submitted a couple years ago. Once from me is enough. If it ever reappears on lobste.rs, it will be because of someone else.

                                                  2. 3

                                                    I’d like to like Rust, but it seems a complex language.

                                                    I’m not talking about the borrow checker, but the large number of features it proposes.

                                                    There must be a way to get a good statically typed safe language that is simple… or not?

                                                    1. 2

                                                      Which features do you think can be removed without sacrificing safety while maintaining usability?

                                                      1. 2

                                                        What a wonderful and deep question!

                                                        The honest answer is: I don’t know. :-)

                                                        However I usually do not consider simple anything that qualify as syntactic sugar (eg macros). Generics are useful but increase the language complexity. Same goes for Traits or Closures.

                                                        Note that I like each of these features.

                                                        But I would like more a safe language where there is always one single obvious way to do each thing.

                                                        1. 5

                                                          But I would like more a safe language where there is always one single obvious way to do each thing.

                                                          Have you used Rust in anger yet? It is not that far from this ideal. Certainly closer than a lot of other languages I have experience with, although I think perfection on this front isn’t really attainable.

                                                          Macros are a good candidate to remove, in theory. But it’s a classic example of letting perfection be the enemy of good. Simple use of macros saves me a lot of annoyance, especially when writing tests. I’ve managed to stay away from more complex uses in all but one instance in my four years of working in Rust nearly daily. Not everyone has avoided them though.

                                                          I don’t see how you could get a safe language with low level control without generics though. I can almost live happily without generics in a language like Go, but not Rust. Traits come right along with that as the implementation of useful generics (not the only choice, but you need something).

                                                          I’m surprised to see you mention closures. They are as standard of a feature as they come.

                                                          Keep in that Rust isn’t just after safe and static types here. We also want zero overhead abstractions. Combining all three creates a sharp knife that cuts through the design space.

                                                          Note that I can’t actually answer my question. I am way too buried in Rust to have the requisite perspective. But. I can at least challenge others on it. :)

                                                          1. 1

                                                            I can at least challenge others on it.

                                                            And you are welcome! I did not tried Rust on any real world project, so my opinion is pretty weak, based on general language considerations, reading the book and few toy programs (written before Rust 1.0).

                                                            Macros are a good candidate to remove, in theory. […] I’ve managed to stay away from more complex uses…

                                                            That’s a smell of a design issue: people should not need to pick “the good parts” of a language. ;-)

                                                            I don’t see how you could get a safe language with low level control without generics though. […]

                                                            By simple composition.

                                                            You might object that it’s boring and verbose, and you’re probably right.

                                                            But it make the language simpler.
                                                            Note that I’m not advocating the Go approach, which is another complex language, just with a different kind of complexity.

                                                            I’m surprised to see you mention closures.

                                                            Again, a cool feature on their own, but they further increase the language complexity.

                                                            And again, without them, programs will be more verbose.
                                                            But language’s verbosity might force the programmer to keep things simple and small.

                                                            We also want zero overhead abstractions.

                                                            And this is another thing I like about Rust. But, while I know nothing about language design (and it’s pretty out of my sphere of competences) my insight is that it could be simpler without sacrifying safety or zero overhead abstractions.

                                                            Note that such complexity is not for free: it locks the language to one single LLVM based implementation, that is hard to port to new hardware or new operating systems.

                                                            1. 4

                                                              That’s a smell of a design issue: people should not need to pick “the good parts” of a language. ;-)

                                                              I don’t think so. To be clear, that’s what I meant by letting perfect be the enemy of good. No design is perfect, just like no battle plan survives first contact with the enemy. Many uses of macros do paper over shortcomings in the expressivity of the type system, but fixing that requires more features, like higher kinded polymorphism or integer generics or specialization. I think you’d say all of those things are too complex. Some of those things are or will be coming to Rust, and macros have allowed, in certain circumstances, folks to solve problems that maybe they wouldn’t have otherwise.

                                                              By simple composition.

                                                              Note that Rust’s type system already favors composition today, in a way that works well with generics. (i.e., Rust does not have something like class inheritance.)

                                                              You might object that it’s boring and verbose, and you’re probably right.

                                                              But it make the language simpler.

                                                              Very strongly disagree, and my counter argument has nothing to do with “boring” or “verbose,” and everything to do with feasibility. A fundamental design proposition of Rust is that folks can build safe APIs that may or may not use unsafe internally. This is very common in the implementation of generic data structures like hash maps, btrees and growable arrays (vectors). If you didn’t have generics, I don’t know how you would build a single implementation that could be reused for data structures like that in a way that has zero overhead. Rebuilding those data structures isn’t practical, since getting the best performance possible requires unsafe code.

                                                              You can thumb your nose at this and just say these are symptoms of design flaws, sure, but removing generics would certainly not make using the language simpler. It would be much much harder and more complex. What would be more interesting is to suggest the removal of generics and also provide an idea on how to solve the problem of reusability. Because of the desire to limit the scope of unsafe, reusability is a critical aspect of writing code in Rust, because you really do not want to be re-litigating the implementation of unsafe code. It is much better to do it once, carefully audit it, and get as many others to use it as possible.

                                                              You might say that dealing with unsafe in this way is itself a flaw. And that the language should be able to permit more things safely. Maybe. But I think that’s an open question at this point, and almost certainly comes with extra type system expressivity, which may make things more complexity. But, that is certainly something Rust can improve on in the future. Nevertheless, it’s hard for me to see an alternative to the core design principle of building safe APIs over unsafe internals.

                                                              Note that such complexity is not for free: it locks the language to one single LLVM based implementation, that is hard to port to new hardware or new operating systems.

                                                              No it doesn’t?

                                                              Anyway, I guess this conversation has run its course. I’d definitely encourage you to keep thinking about concrete suggestions to make a different programming language that accomplishes what Rust does, but in a way that’s simpler. I can maybe imagine some tweaks here and there, but I think omitting some of the big picture features you’ve listed (without replacing them with something else) would be a serious mistake. The fact that you don’t want closures hints to me that we may have fundamentally different opinions that cannot be overcome.

                                                              1. 2

                                                                Wow! :-D

                                                                I think you have largerly overestimated my competence about language design tradeoffs!

                                                                But frankly, given the quality of your answer, I do not regret the misunderstanding. Thanks! ;-)

                                                                All you say is reasonable (and informative).
                                                                And despite being fluent in several different language, I’ve no real clue to propose alternatives. To me Rust feels complex. Not really difficult, just complex.

                                                                But (and sorry if I poke you more, hoping for an even greater response :-D), all the features I named can be defineds as (properly checked) syntactic sugar.

                                                                Whatever you can do with macro, generics and even closures, you can do manually. And whatever you can do manually, you can do by generating code.
                                                                The point is, actually, if you can write or generate boring and safe code.

                                                                I agree that without such syntactic sugar, some programming tasks become overly verbose.
                                                                And boring. But this might discourage complex programs, which is a Good Thing (TM).

                                                                Note that such complexity is not for free: it locks the language to one single LLVM based implementation, that is hard to port to new hardware or new operating systems.

                                                                No it doesn’t?

                                                                Do you mean that we have a Rust compiler not based on LLVM IR?

                                                                I missed that! What a great news!

                                                                1. 4

                                                                  To me Rust feels complex. Not really difficult, just complex.

                                                                  I see. I understand that, yes. I guess the thing that is more interesting to me is whether it is necessary complexity or incidental complexity. If it’s necessary, then great. We need to pick ourselves up by the bootstraps and find a way to teach people that complexity until it becomes easier to learn. If it’s incidental, then the design needs more work. Of course, all designs will have some amount of incidental complexity, but if there’s a huge area of the design space that Rust is missing, then it might be possible that there is in fact a lot of incidental complexity! Or perhaps changes in hardware in the future will alter the calculus we use today to decide things. It has certainly had an impact on C’s design trade offs.

                                                                  And whatever you can do manually, you can do by generating code.

                                                                  Note that generics are, to some extent, an interface to a code generation tool. It is through an automatic process called monomorphization, but that’s basically all it’s doing internally: code generating. There are other approaches to generics that languages like Java take that don’t necessarily require code generation, but they tend to have performance trade offs that would be unacceptable in a language like Rust.

                                                                  Regardless, for Rust, I’m very skeptical that a simple code generation tool would be feasible in practice, but it is of course untested.

                                                                  Do you mean that we have a Rust compiler not based on LLVM IR?

                                                                  Note that you said that it “locks” the language to one single LLVM based implementation, but there is nothing about Rust that makes it true. A different statement that I might agree with is, “The complexity of the language makes it difficult to build alternative implementations.” :-)

                                                                  With that said, there is actually a Rust compiler that doesn’t depend on LLVM called mrustc. I believe it compiles to C. It is obviously a work in progress, but IIRC, it has bootstrapped rustc itself, which makes it worth taking serious IMO. :-)

                                                                  1. 2

                                                                    And despite being fluent in several different language, I’ve no real clue to propose alternatives. To me Rust feels complex. Not really difficult, just complex.

                                                                    Yeah, Rust is relatively complex. I like the language, and I get a huge amount of leverage out of it, but I think there are some things that could be simplified, combined, and made more powerful and general in a future incarnation of a systems language. But the bones of Rust is still far simpler than C++, and much saner than C, and it has a great ecosystem, so it’s the one I pick!

                                                                    I’m putting my money where my mouth is by tinkering on some ideas for future langs, but don’t be expecting anything this decade! Building languages is hard, especially ones that try to tackle the domains that Rust works in.

                                                    1. 8

                                                      Expression-oriented languages make single-entry, single-exit much easier. I often use this style in Rust for instance, because I know that the code is much easier to understand if you can build up the flow chart in your head. It’s not a hard-or-fast rule though - sometimes it is clearer to early return. And the try ? operator does early returning under the hood, and it definitely helps readability.

                                                      1. 4

                                                        If you look at the success of the internet (beyond just the web) I think it’s safe to say OO, not FP, is the most scalable system building methodology. An important realization that Alan Kay emphasizes here is that OO and FP are not incompatible at all. A formal merging of FP and OO can be seen with the Actor Model by Carl Hewitt

                                                        In other words, I think FP can supercharge OO and it seems the rock stable and fast systems built with Erlang and friends prove this out.

                                                        1. 7

                                                          I think servers have scaled now based on solid messaging protocols that are not OOP in nature. And databases are still relational last i checked.

                                                          1. 6

                                                            Alan Kay would say that OOPs foundation is messaging protocols.

                                                            1. 2

                                                              precisely ! The whole internet is an objective oriented system. The smallest model of an object is a computer. so what is an object ? Its a computer that can receive and send messages. Systems like erlang have million little computers running on one physical computer for instance.

                                                              1. 4

                                                                that’s a real stretch. I might as well claim that REST’s success is entirely because it is really just functional programming as it passes the state along with the function and that it is pretty much just a monad.

                                                                Also, SQL is still king and no object-oriented database approach has supplanted it.

                                                            2. 4

                                                              They use the FSM model. Hardware investigations taught me they’d fit in Mealy and Moore models depending on what subset of protocol is being implemented or how one defines terms. Even most software implementations used FSM’s. Maybe all for legacy implementations given what I’ve seen but there could be exceptions.

                                                              And, addressing zaphar’s claim, their foundation or at least abstracted form may best be done with Abstract, State Machines described here. Papers on it argue it’s more powerful than Turing model since it operates on mathematical structures instead of strings. Spielmann claims Turing Machines are a subset of ASM’s. So, the Internet was built on FSM model which, if we must pick a foundation, matches the ASM model best even though the protocols and FSM’s themselves predate the model. If a tie-breaker is needed for foundations, ASM’s are also one of most successful ways for non-mathematicians to specify software in terms of ease of use and applicability.

                                                              1. 3

                                                                You just made the engineer inside me happy :) FSM are the first thing we learned in engineering school but too often software is just hacked together based on code and not design. FSM form the basis of any protocol/service. eg: TCP, FTP, TLS, SSH, DNS, HTTP, etc.

                                                                1. 3

                                                                  The cool thing is those can be implemented and verified at the type level in dependently typed functional languages. See Idris’ ST type. Session types are another example. Thankfully I can see movements in the FSM direction on the front end with stuff like Redux and Elm, but alas it will be a while before these can be checked in the type system.

                                                            3. 4

                                                              I don’t think the internet is a good reference model. IMO the internet is largely a collection of “whatever we had at the time” with a sprinkle of “this should work” and huge amounts of duct-tape on top. The internet succeeded despite being build on OO, not because of it. Though I think FP would also have made the internet succeeded in spite of it, not because of it.

                                                              There is no one true methodology, I think it’s best if you mix the two approaches where it makes sense to get the best of both worlds.

                                                              1. 1

                                                                Let me be more specific , by internet i mean TCP/IP and friends , not HTTP and friends.

                                                                1. 2

                                                                  Even TCP/IP and friends is a lot of hacks and “//TODO this is a horrible hack but we’ll fix it later”. HTTP is just the brown-colored cream on top of the pie that is the modern internet.

                                                                  It’s why DNSSEC and IPv6 have seen such little adoption, all the middleboxes someone hacked together once are all still up and running with terrible code and they have to be fully replace to not break either protocol.

                                                                  I’ve seen enough routers that silently malform TCP packets or (more fun) recalculate the checksum without checking it, making data corruption a daily occurence. Specs aren’t followed, they’re abused.

                                                                  1. 2

                                                                    And yet the internet has never shut down since it started running with all its atoms replaced many times over. Billions of devices are connected and the whole system manages to span the entire planet. It just works.

                                                                    It’s an obviously brilliant and successful design that created tens of trillions of dollars in value. I think you will be hard pressed to find another technology that was this successful and that changed the world to the degree the internet has.

                                                                    Does it have flaws like the ones outlined? Yes of course. Does it work despite them? Yes!

                                                                    The brilliance of the internet is that even when specs are not followed, the system keeps on working.

                                                                    1. 2

                                                                      I think it’s more in spite of how it was built and not because of it.

                                                                      And the internet has shut down several times by now, or atleast large parts of it (just google “BGP outage” or “global internet outage”)

                                                                      It’s not a brilliant design but successful, yes. It’s probably just good enough to succeed.

                                                                      Not brilliant, it merely works by accident and the accumulated ducttape keeps it going despite some hiccups along the way.

                                                                      If the internet was truly brilliant it would use a fully meshed overlay network and not rely on protocols like BGP for routing. It would also not have to package everything in Ethernet frames (which are largely useless and could be replace with more efficient protocols)

                                                            1. 11

                                                              This problem is largely solved by “Jump To” in an IDE (or fancy editor). This sort of thing is why I no longer do real work in languages without these niceties. I just don’t have the patience for it any more.

                                                              1. 5

                                                                Code reviews and online examples can suffer though - I have a very hard time reading unfamiliar Haskell and Agda code on Github where definitions aren’t either explicitly imported in an import list or given a qualified name. But perhaps that’s an argument for better online tooling…

                                                                1. 2

                                                                  That’s a good point, although I agree that better tooling is probably the answer, particularly since fully-qualified imports still mean you’re stuck tracking down docs and such in code review with most of the existing tools.

                                                                  1. 1

                                                                    I have to admit, I fully agree with brendan here. Fully qualified imports really do increase readability to any new, or even old code.

                                                                    I don’t think better tooling is the best approach, I find explicit versus implicit generally explicit ends up being clearer.

                                                                    A possible middle ground is allow ONE unqualified import only as (if i remember right, only skimmed docs) purescript does. That would at least remove ambiguity as to where something could be coming from.

                                                                  2. 1

                                                                    Haskell’s Haddock supports hyperlinked source and so does Agda.

                                                                  3. 4

                                                                    you don’t even need that much; I find vim’s split-window feature is perfectly usable if I want to read the code where something is defined, or look at both the current code and the top of a file simultaneously. whereas on the flip side I know if no good way to eliminate the visual clutter caused by fully qualified names everywhere.

                                                                    1. 4

                                                                      You also can generate ctags and use them in vim ;)

                                                                      1. 1

                                                                        true :) I used to do that more often in my c++ days; somehow I lost the habit now that I’m doing python at work and ocaml at home, even though ctags would probably help with both of those.

                                                                      2. 3

                                                                        this doesn’t solve the “import all names” problem that you hit in languages like Python where some people do import * or you are importing something that was already re-exported from another location. You end up with busy work that an IDE could handle with a name lookup

                                                                        Though I agree that once you find the definition, split windows is a pretty nice way to operate

                                                                        1. 1

                                                                          I too find the result to be cluttered. But I also find new programming languages/syntaxes to be strange and chaotic in the same way. Once I use the language long enough, I am no longer overwhelmed. My hypothesis is that the eye will adapt to fully qualified names everywhere in the same way.

                                                                        2. 1

                                                                          I came here to say just this: with a sufficiently smart editor (vim, ide, or otherwise) this problem goes away almost entirely.

                                                                          That said, I think there are some arguments to be made for always-qualified imports

                                                                          1. 6

                                                                            I think it can be a cultural thing as well. I never enjoy typing datetime.datetime but don’t mind collections.namedtuple. itertools.ifilter is annoying though. Redundant words or information looks and reads bad.

                                                                            When the culture is to assume qualified imports, then the library will always be used to provide context, and that can be quite nice.

                                                                            When resolving a qualified name is the same syntax as a method call, that can look bad quickly. Python very much suffers from this problem. Think of Clojure stylebanespace syntax as an alternative.

                                                                          2. 1

                                                                            Does “Jump To” actually jump you to the import declaration or the function definition? I’ve never used an IDE. My guess is that an IDE would largely eliminate manual identification of the function’s origin. So that’s useful! But I’m not convinced that this would be faster than reading the module name inline in plain text. No keystroke or mouse click required to get the information. I guess the argument for using an IDE to solve this problem is something like the IDE places the information close at hand while also enabling a less verbose code style. That’s a reasonable argument. At some point the conversation becomes purely a debate about the pros and cons of IDEs. Then I would say that it’s nice to have code that doesn’t incur a dependency on a code editor.

                                                                            1. 2

                                                                              You can jump to the declaration in most IDEs (provided there is support for the language). In many you can also view the documentation for a symbol inline, no need to go searching in most cases. I agree with you that this really just changes the debate to one about tooling. However, since many people (myself included) prefer the readability of unqualified imports, tooling support is important to at least think about. For example, I work in Dart a lot at work, the Dart community tends toward unqualified imports because, at least in part, I think, pretty much everyone who writes Dart code uses either an IDE or an editor plugin with IDE-like features.

                                                                          1. 6

                                                                            A trivial counterexample to this is inheritance for actors in games, e.g., “EliteMook” extends “Mook”, adding smarter target selection or something.

                                                                            1. 7

                                                                              Games these days will often drop inheritance in favour of using an entity component system or data oriented approach. This is much more cache friendly than the more naive taxonomic inheritance hierarchy, especially when you have thousands or tens of thousands of entities you need to process.

                                                                              1. 2

                                                                                Quite right, at scale.

                                                                                For small games, or for games that don’t have a gigantic update loop, a taxonomic approach you’d see from OOP works fine.

                                                                                My example wasn’t the end-all-be-all, it was just a case where inheritance works pretty nicely. No approach, of course, is going to work forever.

                                                                              2. 5

                                                                                I found it interesting that Eiffel is dropped into the article but 6 additional variants on inheritance are described by Bertrand Meyer:

                                                                                https://archive.eiffel.com/doc/manuals/technology/oosc/inheritance-design/section_05.html#HDR8

                                                                                1. 7

                                                                                  Because Eiffel imposes that preconditions are contravariant and preconditions are covariant with inheritance, it does impose “subclass means subtype” out of all of the possibilities.

                                                                                  1. 6

                                                                                    The ironic thing is that while Eiffel preconditions are contravariant, the argument types are covariant. That leads to type errors at runtime and an extremely ugly hack in the Eiffel language to get around it.

                                                                                    1. 1

                                                                                      I’ve never programmed in Eiffel so I have no insight into the language specifically. I only meant to point out that the author’s ontology of inheritance is not comprehensive.

                                                                                      1. 2

                                                                                        think of it as a minimal reproduction case for the bug.

                                                                                  2. 4

                                                                                    Even that counter-example breaks- what happens when the behavior that makes an EliteMook different than a regular Mook also needs to be incorporated into bosses- we have an EliteBoss now. Which sort of touches upon what the article is talking about, because really we’re talking about different kinds of inheritance.

                                                                                    1. 4

                                                                                      the existence of a counterexample is merely lucky happenstance, when the thesis is “most of the time this situation is not true”. Particularly the ontology of a computer game (or indeed any game; D&D went for decades before multiple inheritance was added) can be more simplistic than one for a business process.

                                                                                      1. 4

                                                                                        Perhaps a more accurate title would’ve been in order “Why inheritance often does not make sense.”

                                                                                    1. -1

                                                                                      Funny, this has a name, Point Free Style

                                                                                      1. 4

                                                                                        This is not really the same thing. Here we’re talking about folks extracting local variables into methods unnecessarily.

                                                                                        1. 1

                                                                                          Pointless Free Style

                                                                                      1. 4

                                                                                        This is a super nice, language agnostic talk that goes through some of the human aspects of adopting functional programming in a large organization. I like how Ken both acknowledges that technology is really important, but also that technology alone is not enough to make change in an organisation, you need to invest time helping people take on that journey as well, or you will fail and potentially even do more harm than good. He also goes though a number of (abstract) case studies and antipatterns he’s seen - lots of good food for thought!

                                                                                        1. 4

                                                                                          The new type checker, based on a constraint-generating but bidirectional approach, can type a lot more programs than the older, Algorithm W-derived checker. As an example, consider the following definition. […]

                                                                                          OCaml (which uses rather standard Hindley-Milner-Damas type inference) can type-check this definition just fine.

                                                                                          val map : ('a -> 'b) -> 'a list -> 'b list
                                                                                          
                                                                                          1. 2

                                                                                            hindley-milner or bust

                                                                                            1. 2

                                                                                              That isn’t a rank-n type. Things become much trickier once you start nesting foralls, which is where bidirectional type checking shines.

                                                                                              1. 2

                                                                                                Certainly, but still I am a bit confused by this blog post that seems to imply that this function is not typable with traditional HM algorithms for prenex polymorphism, while it does seem to work just fine. Is there an error in the blog post itself, and the author meant to insert an example making essential use of higher-order polymorphism? Or was there a bug in the previous type-checker that could have been fixed while keeping the same inference specification, instead of moving to higher-rank bidirectional algorithms?

                                                                                                1. 3

                                                                                                  Perhaps I didn’t make this clear enough, but I did not mean to imply this was a fault with Algorithm W in general, but rather a fault of my implementation of HM inference.

                                                                                                  Or was there a bug in the previous type-checker that could have been fixed while keeping the same inference specification, instead of moving to higher-rank bidirectional algorithms?

                                                                                                  Indeed, but I chose to do both at once. (I had previously tried to add rank-N polymorphism to the old type checker before figuring out it was broken beyond saving)

                                                                                                  1. 2

                                                                                                    I see, thanks for the clarification!

                                                                                                    Edit: Also, I only realize now but: when you write “ε-contraction” (epsilon-contraction), you mean “η-contraction” (eta-contraction), as a transformation from (fun x -> t x) to just (t), right?

                                                                                                    1. 1

                                                                                                      Er.. Yes, thanks. Greek was never my forte, maybe I should stick to the Latin alphabet. (Edit: fixed.)

                                                                                            1. 15

                                                                                              I was hoping for something else from this.

                                                                                              Given the title, I was hoping for a deep insight into the tradeoffs of macros and why those tradeoffs are worthwhile in Rust. Instead, its more of a listing of “things you can do with Rust macros”. There’s nothing wrong with that.

                                                                                              I’d really love to see a deep pros/cons of macros in Rust. There probably aren’t many people who could write what I’d like to see though. You’d need a deep understanding of macros across a variety of languages, a strong grounding in programming language theory, and a strong grounding in Rust.

                                                                                              1. 10

                                                                                                Rust’s macros currently don’t really work nicely with the module system - they all live in a global namespace and you have to use the odd #[macro_use] and #[macro_export] attributes to import and export them. It’s quite ugly. This will be fixed in Macros 2.0 though - from what I understand they are learning from Racket - I think it might be called ‘staged macro compilation’ or something… it is a non-trivial problem to solve though. Also, macros at the top level are not hygenic, I think this will also be fixed in Macros 2.0.

                                                                                                Another annoyance is the APIs surrounding procedural macros - it’s not as nice as working with Racket-style reader macros. You’re more working with a token stream, rather than an AST. Also, the API does not enforce hygiene as far as I know. Again, this should also be fixed in Macros 2.0.

                                                                                                Another frustration is that they don’t work nicely with rustfmt - because rustfmt doesn’t really know how the original macro author wanted to format them.

                                                                                                Other issue is that they have a restricted syntax that doesn’t allow you to create really nice looking language extensions. So a match! replacement would have to look like:

                                                                                                my_match!{ expr;
                                                                                                    pattern1 => expr1,
                                                                                                    pattern2 => expr2,
                                                                                                }
                                                                                                

                                                                                                Rather than:

                                                                                                my_match! expr {
                                                                                                    pattern1 => expr1,
                                                                                                    pattern2 => expr2,
                                                                                                }
                                                                                                

                                                                                                This is because we want tooling to have to understand macros in order to parse Rust code. I don’t think there is any solution for this.

                                                                                                All in all, I think macros a a nice addition to Rust, but they still feel a little ‘bolted on’ to the language, and could do with some improvement in the future. The Rust team knows this, and they are working hard to make those improvements!

                                                                                                1. 1

                                                                                                  Thanks @brendan!

                                                                                                  1. 5

                                                                                                    No worries! I’m not super up-to-speed with macro theory, alas. But here are some RFCs (alas no prior art or references to the literature are given):

                                                                                                2. 5

                                                                                                  This comment is a pretty good rundown of issues with the current macro approach.

                                                                                                  The list is pretty good, I’d only add the call-site syntax (!) to the list of problems.

                                                                                                  1. 2

                                                                                                    Just something I’ve observed, some of the popular languages are either strongly typed but have macros, or loosly typed and don’t have macros. ie. C/C++ vs Javascript. However, Java is strongly typed but doesn’t have macros, so take my observation with a grain of salt :)

                                                                                                    1. 7

                                                                                                      My intuition is that everybody hates boiler-plate, but static languages tend to the problem with macros, while dynamic languages address the problem with reflection and runtime meta-programming.

                                                                                                      Java is a notable outlier in that it addresses the boiler-plate problem with IDEs.

                                                                                                      1. 1

                                                                                                        “Java is a notable outlier in that it addresses the boiler-plate problem with IDEs.”

                                                                                                        Smalltalk partly solved boilerplate with the IDE and live coding. In Java ecosystem, boilerplate was the solution to C/C++‘s problems. The IDE’s then ensure that various modules have correct amounts and types of boilerplate. Or something like that.

                                                                                                      2. 1

                                                                                                        Haskell has no macros but Scheme does. Anyway, strong typing prohibits runtime type inspection and this leads to some repetitive boilerplate code, for example to derive serialisation code from type declarations. Macros can make this simpler. In the case of type-driven code, type classes (Haskell) are powerful enough to not require macros. Another case for macros is code instrumentation and here I believe type classes would not be enough.

                                                                                                        1. 5

                                                                                                          Haskell does have macros - they’re called (somewhat confusingly) ‘Template Haskell’. I prefer type-directed code generation using type classes, but this is often at the expense of compilation time, when compared to Template Haskell. Hopefully this will be improved at some stage…

                                                                                                          1. 1

                                                                                                            Where are you putting TemplateHaskell in this categorization? I believe it’s the recommended way to handle lenses, and is also used in the Yesod ecosystem.