1. 30
  1.  

  2. 9

    Literally so happy to see recognition for structural types and row types!!! The coolest part of this which isn’t necessarily emphasized in the article is that these types are inferred in both OCaml and Crystal.

    1. 5

      Very nice descriptions and clear examples. Row polymorphism is brought up a lot when discussing e.g. the deficiencies of Haskell’s built-in records, or algebraic effect systems, but these examples have more of an OO flavour than a functional one.

      I’ve not actually used OCaml (although I have used StandardML), but I often hear that its users tend to avoid its OO-style features. Are those examples indicative of how real OCaml users would write code, or would they tend to use some other approach (e.g. modules)?

      1. 2

        Yeah almost no one uses objects in OCaml nowadays. They use dynamic dispatch for method calls and so tend to be slower than non-object-oriented OCaml code.

      2. 4

        I’m surprised there is no mention of Go in the article. Crystal looks attractive, though.

        1. 4

          Structural typing is one of my favorite parts of Pony. It can come in very handy when you need it.

          At this point, I’d be sad to use a language that doesn’t have the option of both Nominal and Structual typing.

          1. 3

            It’s a good article. One drawback is the author has to stop periodically to explain Ocaml’s syntax. It’s probably weird to developers coming from Java or Ruby. I think this article’s accessibility could be improved by eliminating the Ocaml examples in favor of Java, Ruby, or pseudocode with similar syntax. It could still mention that Ocaml supports the features in case people want to check it out, too.

            1. 3

              What’s also cool is when you let the object itself decide to report its shape. In Objective-C, the default behaviour of an object sent a -conformsToProtocol: message is to check whether it or a superclass was compiled with support for the protocol, but that could be overridden. So in MacRuby they built a runtime version of a structural type: do I implement every required method specified in the protocol? If so, I respond yes when asked whether I conform to it.

              Pedantically this is inexact (it’s possible that the methods are implemented, but not with the contract expected by collaborators of the object implied by the protocol) but in reality it’s plenty good enough.

              1. 1

                I don’t understand why everyone is so fond of duck typing. I have been programming in Ruby for 8 years and I feel I could easily live without duck typing. The vast majority of the functions I have written are only used with a single type of argument. In the rare cases where that is not so, it’s because there is regular class inheritance in play.

                1. 3

                  Duck typing makes iteration & casual experimentation faster, since you don’t need to consider the structure of an object prior to first implementing it, & can change its implementation gradually without rewriting other parts of the code, even in a REPL. This makes a lot of sense for ‘small computing’ (i.e., where the goal is to prototype with as little pain as possible), but is less useful for ‘big computing’ (i.e., where the goal is solid performance and reliability even when millions of potentially-malicious users are throwing requests at you).

                  Type inference in general is great for ‘small computing’, and being able to combine type inference with duck typing means that you get all the reliability benefits of compile-time strong typing with all the developer-side flexibility and reduced cognitive load of being able to ignore types entirely.

                  Nominal types, when they are combined with type inference, can also be great for reducing cognitive load on developers. (See: your average haskell program, where almost no computation is actually being done because so much stuff is basically offloaded to the type system.) But, these strengths conflict with each other: each mostly benefits the developer by allowing them to forget or ignore the other.

                  1. 1

                    I’m not sure we’re on the same terminological page here. What you describe sounds to me like the advantage of dynamic typing in general. To me ducktyping means deliberately designing methods that take completely unrelated types that happen to share a method. Not changing what you pass to a method while developing and having it still work. That’s just the advantage of dynamic typing, which subsumes ducktyping in such cases.

                    1. 2

                      Only in nominal typing does the idea of types with shared members being ‘unrelated’ make sense. Outside of nominal typing, whether typing is static or dynamic, the shape of a type determines its compatibility while its name is just a convenience for documentation purposes.

                      Structural typing is static, in that type checking is performed at compile time, but it is non-nominal, in that types are determined at compile time to be equivalent if they have the same members. In other words, it’s as though we have type checking based on interfaces, but with the compiler defining the contents of those interfaces based on what members are used.

                  2. 3

                    Duck Typing or Typeclasses let you get the polimorphism benefits of class inheritance without needed classes or inheritance.

                    This is very useful, especially when extending something 3rd party. So long as I know what the 3rd party thing expects, I can build an interface to my stuff it can use. Without them having set up their classes to intentionally support this.