Ufuk Hacıoğulları
Ufuk Hacıoğulları

Reputation: 38478

Is a polymorphic record possible in Erlang?

I was reading about polymorphic types and the only example is for a tuple:

type queue(Type) :: {fifo, list(Type), list(Type)}.

I have a record like this:

-record(heap_node, { item :: any(), children :: [#heap_node{}] }).
-type heap_node() :: #heap_node{}.

Right now I can't pass a type parameter to the record. Is it possible in Erlang?

Upvotes: 2

Views: 242

Answers (2)

zxq9
zxq9

Reputation: 13154

-type queue(Type) :: {fifo, list(Type), list(Type)}.

You would want to maybe represent this as:

-type queue(term()) :: {fifo, [term()], [term()]}.

That does not mean that these are consistent terms. term() is an alias for any() and just means that "you have a list of things", not a "list of homogenous things".

The more important question here is to ask what you are trying to do? What is the overall effect you are trying to achieve? The Erlang standard library has a large number of data structures that handle things like queue, bags, collection, sets, maps, trees, etc. quite effectively and efficiently.

The cases where you have a specific need, of course, is where you write you own. Every time you have a specific need you will find yourself either needing wide-open types (like term()) or strict types (like non_neg_integer() or #{integer() := pid()}).

If we know a little more about the overall effect you're trying to achieve then it may be possible avoid expending a lot of effort reinventing OTP or the stdlib.

About Erlang's philosophy

Keep in mind that Erlang is not an academic language. It is a practical industrial language that suffers academic concepts as far as they are a natural outcome of effective language and system design -- in the era it was created.

It is:

  • Functional -- but only because that is the most effective way to keep your head on straight when writing real-world programs that are massively concurrent and would be impossible to reason about with shared-state OOP.
  • Adheres to "the actor model" -- but predates the term and therefore cannot be said to be an "implementation of it". It looks like an actor model because once again, that is the only sane way to model a massively concurrent system and represent clear partitions among processes.
  • Reactive -- but predates that term as well (as applied to software as a buzzword). This is an incidental outcome of using a message passing model.
  • Has a type system -- but that grew up later as a positive side effect of being a functional language. But it is not a "pure" functional language because side effects are the point of most programs written in it, and the type system must be weak because of the way the runtime handles dynamic calls and spawns and whatnot.

(Incidentally, most other hot buzzwords you might think of also happen to describe Erlang, but this is once again only incidental. Erlang just happens to be fully buzzword compliant, and likely will be for a few more decades.)

That means Dialyzer is a permissible typer, not a strict typer -- it assumes type correctness until it can prove otherwise. Strict typers go the other direction, assuming type faults unless it can prove or infer otherwise. So no polymorphic types, as everything in the program is assumed to be polymorphic until type constraints can either be inferred through analysis or made explicit via the type language. You won't find as much power in the Erlang type system as in Haskell, for example -- but you will find that the system overall is geared specifically to get real-world programs out the door, though your mindset in development may be a little different.

See also: dialyzer not detecting guard violation when function is exported

Upvotes: 3

legoscia
legoscia

Reputation: 41578

You can create a type that is polymorphic in item:

-type heap_node(A) :: #heap_node{item :: A}.

But A wouldn't carry over to the children field - those heap_node records would still have an unrestricted item field.

I tried using the type recursively:

-type heap_node(A) :: #heap_node{item :: A, children :: [heap_node(A)]}.

But Dialyzer didn't like that:

dialyzer: Analysis failed with error:
foo.erl:9: Illegal declaration of #heap_node{children}

Upvotes: 4

Related Questions