Live data from Hacker News

OCaml 4.03: Everything else

blogs.janestreet.com

41–50 of 84 posts

Re: OCaml 4.03: Everything else

#41
post #28

Earlier quoted context omitted.

What if I want some hybrid? From OCaml, I want the awesome module system and strictness by default. (I'm not dismissing laziness or any other evaluation strategy - just saying strictness is a better default.) From Haskell, I want effects tracked in types, higher-kinded types and painless parallelism.

Scala seems to do all these. Unfortunately it also has a bit of a messy OO system that can make inference more painful than the aforementioned options, but life's about trade offs I suppose...

Scala offers awkward encodings for some of the things you can do with ML modules, but sometimes it falls short. For instance, you can't emulate datatype declarations inside signatures (as opposed to inside modules themselves). Or SML-style datatype replication. Or OCaml-style applicative functors.

Lack of global type inference is far from the only problem with Scala.

Re: OCaml 4.03: Everything else

#42
post #32

Earlier quoted context omitted.

What if I want some hybrid? From OCaml, I want the awesome module system and strictness by default. (I'm not dismissing laziness or any other evaluation strategy - just saying strictness is a better default.) From Haskell, I want effects tracked in types, higher-kinded types and painless parallelism.

It's easy to hard to force Haskell into "not being lazy". I'd say Haskell in lazy-by-default. You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript. A better module system for Haskell is in the makings (Backpack by EZ Yang), but may or may-not be what you are looking for. To my understanding the module system of Haskell is not very limiting at all.

> You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript.

In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening.

> To my understanding the module system of Haskell is not very limiting at all.

Even Haskell's own designers admit otherwise. Haskell actually originated as an attempt to standardize a lazy, purely functional language for the research community. Modules and records weren't very high in the priority list, and it really shows. But sometimes behind a black cloud there's a silver lining, and the limitations of records inspired Haskell programmers to invent lenses, which are very, very, very awesome.

But, to this day, Haskell still doesn't have a decent alternative to ML-style modules and functors. As far as I can tell, in vanilla Haskell (no extensions), you can only encode functors on modules with a single type component. With either type families or multiparameter type classes, you can encode modules with more than one type component, but the result is very awkward.

Re: OCaml 4.03: Everything else

#43

Earlier quoted context omitted.

On the contrary, I think OCaml's class system is potentially very confusing to someone coming from mainstream object-oriented languages. Java programmers would be confused the most, because they would have to unlearn the most. In Java, a class is a type on its own right. An object of class (and, hence, type) `Foo` has a very specific data structure, even if this data structure is unknown to the user. On the other han…

Can you provide an example of " This happens if `bar`'s type contains negative occurences of its self-type"

I inverted the roles of `foo` and `bar` with respect to my previous post. In `foo`'s definition, the self-type is `'a`. Note how `foo` has a method that takes an argument of type `'a` - a contravariant occurrence of the self-type. The consequence is that, although `bar` inherits `foo`, their associated structural types are neither a subtype of the other.

    lolcathost% ocaml
            OCaml version 4.02.3
    
    # class foo =
        object (_ : 'a)
          method test (_ : 'a) = ()
        end;;
    class foo : object ('a) method test : 'a -> unit end
    # class bar =
        object
          inherit foo
          method other = ()
        end;;
    class bar : object ('a) method other : unit method test : 'a -> unit end
    # let f = new foo;;
    val f : foo = 
    # let b = new bar;;
    val b : bar = 
    # f#test f;;
    - : unit = ()
    # b#test b;;
    - : unit = ()
    # f#test b;;
    Characters 7-8:
      f#test b;;
             ^
    Error: This expression has type bar but an expression was expected of type
             foo
           The second object type has no method other
    # b#test f;;
    Characters 7-8:
      b#test f;;
             ^
    Error: This expression has type foo but an expression was expected of type
             bar
           The first object type has no method other
    #

Re: OCaml 4.03: Everything else

#44
post #32

Earlier quoted context omitted.

It's easy to hard to force Haskell into "not being lazy". I'd say Haskell in lazy-by-default. You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript. A better module system for Haskell is in the makings (Backpack by EZ Yang), but may or may-not be what you are looking for. To my understanding the module system of Haskell is not very limiting at all.

> You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript. In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening. > To my understanding the module system o…

> In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening.

Unfortunately, the general story isn't quite so nice, due to thread-safety.

Immutable data structures are great for thread-safety, as there's no problems with data races or generally invalidating things, because there's no writes... but laziness introduces secret writes, among other problems. This means a type providing laziness either can't be used across multiple threads simultaneously, or is forced to have extra overhead (e.g. atomic operations, blocking & registering for being woken up when trying to force a thunk that another thread is already forcing), and users of a library definitely need to know if a module is using mutation in the former way, and may want to know about the latter.

Of course, if shared-memory concurrency/parallelism is forgone, this isn't a problem, but that omission has its own downsides. And... I would suspect the overhead of just implementing it in a thread-safe way can be considered negligible in many cases, especially if data is guaranteed to be pointer-sized (or less).

(Concurrency/parallelism are sometimes described as "abstraction breaking" for this sort of reason.)

Re: OCaml 4.03: Everything else

#45
post #44

Earlier quoted context omitted.

> You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript. In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening. > To my understanding the module system o…

> In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening. Unfortunately, the general story isn't quite so nice, due to thread-safety. Immutable data structures are great for thread-safety, as there's no pr…

I don’t understand what you’re saying. Haskell (specifically GHC) has perfectly thread-safe secretly mutable thunks. Are you suggesting there’s a problem with doing this in a strict language? An impure language? Or something else? I don’t see why you’d say there’s a problem with a secretly mutating thunk datatype.

Re: OCaml 4.03: Everything else

#46

I really hope modular implicits will make it to the language one day. While OCaml libraries are no stranger to monads they usually only include `bind` and `return`. Since I'm coming from Haskell I'm used to a vast Applicative and Monad vocabulary and making do with just `bind` and `return` is rather painful. So having a generic library of Monad combinators that one could use with any Monad would be great. Also, being…

> I really hope modular implicits will make it to the language one day

"I'm expecting there will be more news about those in the next six months".

I'd be very surprised not to see modular implicits in 4.04 now that the foundation has been put in place with Flambda[1]:

"Even if you're perfectly happy with OCaml's performance as is, Flambda is still an exciting change. That's because various upcoming language improvements like modular implicits (a feature that brings some of the same benefits as Haskell's typeclasses) will only really perform acceptably well with a good inliner in place".

[1] https://blogs.janestreet.com/flambda/

Re: OCaml 4.03: Everything else

#47
post #44

Earlier quoted context omitted.

> You could do lazy eval in non-lazy-by-default langs by using something akin to `Promisses` in JavaScript. In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening. > To my understanding the module system o…

> In a strict language, a lazy thunk can be represented as a single mutable cell. In ML, thanks to type abstraction, the mutation is confined to the module that implements laziness, and, in the rest of the program, there's no way to tell that anything impure is happening. Unfortunately, the general story isn't quite so nice, due to thread-safety. Immutable data structures are great for thread-safety, as there's no pr…

You have a point. We might actually end up with a multitude of lazy types (akin to `RefCell` vs. `Mutex` in Rust). In high-level languages like ML and Haskell, forcing all lazy data to be boxed, and atomically mutating pointers to boxed data, seems like a reasonable solution.

Re: OCaml 4.03: Everything else

#48

If I were already sold on using Haskell or OCaml for a new project, what would be the big seller for OCaml being the choice? I haven't dug into SML or OCaml and I'm not expert with Haskell yet but from looking at them they don't look substantially distant from Haskell.

You can learn OCaml in a couple weeks.

Re: OCaml 4.03: Everything else

#49

Earlier quoted context omitted.

There's still hope! There are a number of much anticipated features that haven't made it into this release. In particular, the multicore GC, which at one point had been expected to land in 4.03, has been pushed back, likely to 4.04.

and when exactly 4.04 will pop up ? ~~ Well, I know, it's hard. ..

I'm guessing after 4.03 and before 4.05?

Re: OCaml 4.03: Everything else

#50

Could anyone explain ephemerons further, maybe with examples? It seems really interesting.

The wikipedia page explains them pretty well I think, so going there is a better explanation than anything else.

For a programmer, they allow you to be more precise about the point in time where an object should be regarded as a weak object. That is, an object which can be collected when under GC pressure. A weak references edges itself toward this goal, but often require some manual intervention and/or knowledge of the GC world to manage by the programmer. An ephemeron removes this additional knowledge from the programmers mind. It allows one programmer to make an interface which is truly not leaking in GC abstraction. So other programmers don't have to know about it at all.

Post reply on HN