Hacker News new | ask | show | jobs
by anfelor 585 days ago
Partially instantiated data structures are also available in Haskell (via Laziness), in OCaml (via tail modulo cons, https://inria.hal.science/hal-03146495/document) and Koka (via constructor contexts, https://dl.acm.org/doi/pdf/10.1145/3656398)
3 comments

Laziness and TMC are fundamentally different from partial instantiation in that they are implementation details, mostly/wholly invisible to the language semantics themselves.

A key aspect of partial instantiation is that the "holes" may be filled in by a semantically unrelated piece of code, which is not the case for either laziness or TMC (wherein the contents data structure must be defined in one place, even if the implementation does not evaluate it immediately).

(I don't know Koka so I can't speak to that.)

Yes, that is true! Koka's constructor contexts also allow you to do this. A constructor context is a data structure with exactly one hole which you can fill in with an arbitrary value (or even several arbitrary values, copying the constructor context in the process).
Also, you can do lazy initialization in any language that has functions by passing a getter function around instead of a value.

I recently had need for this to implement validation for recursive data structures in TypeScript. It depends on support for forward references in the body of a function. The tricky bit is ensuring that the getter isn’t called until the cycle is completed by defining the forward reference’s target. The type system doesn’t help; you just have to write the implementation so it doesn’t call the getter.

I don't think Haskell can do this, can have a growable linked list for example. 'last a' is 'last a', regardless what is between them (modulo shadowing and such).

And I suspect that Prolog's Partial Instantiation is, while not mutating data, but it is mutating references somewhere

Technically, Haskell laziness is just mutability under the hood :)

And the "difference list" mentioned in the article is also in Haskell - although framed differently (more "functionally")

    type DList a = [a] -> [a]

    concat :: DList a -> DList a -> DList a
    concat = (.)

    toList :: DList a -> [a]
    toList d = d []

    fromList :: [a] -> DList a
    fromList = (++)
Haskell has cyclic data structures, which also can’t be implemented without a mutable reference somewhere, though it may be buried in the implementation.

The difference is being able to define an incomplete data structure (with a forward reference) and then defining the target of the reference at runtime. Most languages will complain about an undefined reference if it’s not defined by the end of a module.

You could do it with soft references, though. Use a symbol or string to refer to something and define it later.

I rather think the fact the the same symbol/string always denotes the same thing is especially helpful for cyclic structures.

Anyway I think I misunderstood the article, I thought they added things to a dictionary in the prolog repl, which would be impossible in haskell/ghci afaik.