1. 15
  1.  

  2. 8

    If you use ghci with the defer typechecking flag you can probably get the experience the author is looking for?

    1. 5

      My experience so far with Clojure’s spec library is that it provides exactly the experience Michael describes here. Start at the REPL to get a sketch together, then “finish” it with specifications of arguments/return values and property-based testing.

      1. 4

        Rust and Haskell (and probably other modern statically-typed languages as well, but those are the ones I’m familiar with) allow you to temporarily bypass some of the “questions the type checker forces you to answer” during the development process, by providing functions that return runtime errors if evaluated but will typecheck when inserted as placeholders into code. These are undefined in Haskell and a number of different constructs in Rust such as the panic! and undefined! macros and some functions that invoke the panic macro like unwrap().

        For instance, if I’m adding a new data constructor to an extant type in Haskell, and I want to test one part of my program without (yet) dealing with all the other parts of my program that will now complain about a non-exhaustive case statement, I can insert something like:

        case myValue of
          ...
          NewVariant -> undefined
        

        to get the program to compile, so I can test all the parts of the system that don’t rely on calling that particular case statement. Eventually, I can come back to this case statement, remove the undefined, and replace it some code that does something useful.

        Similarly in Rust it’s a common idiom to call .unwrap() on some Option<a> or Result<a,err> type to get the a out and keep on coding, without worrying about how to handle the error case just yet.

        1. 1

          Rust and Haskell (and probably other modern statically-typed languages as well, but those are the ones I’m familiar with) allow you to temporarily bypass some of the “questions the type checker forces you to answer” during the development process, by providing functions that return runtime errors if evaluated but will typecheck when inserted as placeholders into code.

          Perhaps the dynamic “type” in C#? It’s mainly for interop with dynamically typed languages, but it effectively does make checking of the type the runtime’s problem, albeit bringing in some other behaviours. For an even more primitive version, you can argue about setting the type as objet and the value as null in many languages; which could work for this purpose.

        2. 4

          Working in Haskell or any other statically typed language, is a different experience

          I’m a fan of dynamic typing, but he should probably try OCaml’s REPL. Aside from lack of vi bindings in utop (which put me off), I feel like it pretty much solves the problem he’s talking about.

          In other words, the REPL thing is not fundamental.

          To me, the what tips the scales in favor of dynamic typing – for most problems – is that:

          1. dynamic languages support metaprogramming better. Type checking generally gets in the way of metaprogramming [1]. However this difference is probably not fundamental either, and hopefully someone will come up with a good answer. I believe multi-stage programming is good research in this direction. [2]

          2. Compilers are generally too slow. I think Go is probably the only one that is fast enough for my taste. I find that in Python I often use unit tests as a “REPL”, and I believe that also works fine in languages like Go where the compiler is fast (although I don’t really use Go).

          [1] http://www.oilshell.org/blog/2016/12/05.html

          [2] https://en.wikipedia.org/wiki/Multi-stage_programming

          1. 2

            The big difference comes from editor integration in languages like Clojure. The REPL isn’t just a toy you use on the side, it’s an integral part of the workflow. A Clojure REPL runs within the actual application you’re working on, and any code you write can be sent to it for evaluation immediately. You can see an example here.

            Incidentally, this is very useful to have in production as well. For example, I’ve had situations where a remote service my application was talking to changed its API, and I was able to update the code to match live with zero downtime.

            1. 2

              Yes that’s true. Python REPLs aren’t commonly used like that.

              Even with R, I don’t think people do that. You tend to have a REPL and your code source. I guess one difference is if you have batch processes vs. persistent processes – whether you are recreating state every time or using the REPL on live state.

              I will be reading that link since obviously a shell is a REPL :)

          2. 1

            As ever, an example would help.