New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sum algebraic data type #258

Merged
merged 10 commits into from May 7, 2015

Conversation

Projects
None yet
4 participants
@yloiseau
Contributor

yloiseau commented Apr 6, 2015

This PR adds sum algebraic data types, also known as tagged union, to Golo
(see http://en.wikipedia.org/wiki/Tagged_union).
Product data types are already provided by struct and tuple.

Usage example

Some well known usages of sum types are:

Enumerations

union Color = {
  RED
  GREEN
  BLUE
}

It is here somewhat equivalent to Java enum, and can be extended through
augmentation.

Option type

union Option = {
  Some = {value}
  None
}

Values can have fields. They are in this respect similar to struct

Lists

union ConsList = {
  List = { head, tail }
  Empty
}

Binary trees

union Tree = {
  Empty
  Leaf = { value }
  Node = { left, right }
}

Augmentations

The union itself is a abstract class, and each possible value is a concrete
final (inner) class extending it. It is thus possible to augment the whole union:

augment Option {
  function map = |this, func| -> match {
    when this == Option.None() then this
    otherwise Option.Some(func(this: value()))
  }
}

or just a value:

augment ConsList$Empty {
  function isEmpty = |this| -> true
  function head = |this| -> null
  function tail = |this| -> this
}
augment ConsList$List {
  function isEmpty = |this| -> false
}

Remark

I want to provide a neat namespace for the generated classes, to allow the
definition of unions with values having the same name in the same module.

Currently, the union itself is an abstract class and each value is an inner
class of the union. The FQN of a value type is thus module.types.MyUnion$Value.
The abstract class provides static factory methods to create the values, used
as MyUnion.Value(fields...)

As an example, a union declaration like:

union Option = {
  Some = { value }
  None
}

generates classes equivalents to:

abstract class Option {

  public static final class Some extends Option {
    public final Object value;
    protected Some(Object v) { this.value = v; }

    @Override
    public String toString() {
      return "enum Option.Some{value=" + value + "}";
    }

    @Override
    public boolean equals(Object other) {
      if (other == this) { return true; }
      if (other == null) { return false; }
      if (!(other instanceof Some)) { return false; }
      Some o = (Some) other;
      return java.util.Objects.equals(value, o.value);
    }

    @Override
    public int hashCode() {
      return java.util.Objects.hash(value);
    }
  }

  public static Option Some(Object value) {
    return new Some(value);
  }


  public static final class None extends Option {
    @Override
    public String toString() {
      return "enum Option.None";
    }
  }

  public static final Option None = new None();

}

An other solution is to use public classes in a sub-package, giving types like
module.types.myunion.Value and to generate factory functions in the defining
module (as it is the case for struct)

I'm not sure what is the more convenient. What do you think?

@yloiseau yloiseau added the feature label Apr 6, 2015

@yloiseau yloiseau added this to the 3.0.0 milestone Apr 6, 2015

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 6, 2015

Comments on syntax and implementation are welcome

@jponge

This comment has been minimized.

Member

jponge commented Apr 6, 2015

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 6, 2015

to be done:

  • tests... (I known, TDD next time)
  • golodoc integration
  • syntax highlighting
  • a golo library of useful unions ?
@danielpetisme

This comment has been minimized.

Contributor

danielpetisme commented Apr 6, 2015

How do you do that.... every you touch is automatically converted into a mathematical concept ! 😉
Hats off

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 6, 2015

@danielpetisme functional programming power! ✌️

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 7, 2015

BTW, I’m not sure about the keyword name, which could be misleading. Possibilities could be:

  • enum : used in Rust and Swift, hence my choice
  • union (unions in C are somewhat similar)
  • variant : used in Boost lib and D
  • sum (also misleading)
  • sumType

Any toughts?

@danielpetisme

This comment has been minimized.

Contributor

danielpetisme commented Apr 7, 2015

I vote for

  • enum (my first thought is java enum but If I'm right is not excatly the same)
  • variant (cause it's a new keyword in my Java world so it force me to open some docs to understand the concept)

Otherwise I could invent a term (I miss pimp 😉 )

@jponge

This comment has been minimized.

Member

jponge commented Apr 7, 2015

enum looks good to me. union, too.

Some well known usages of sum types are:
==== Enumerations

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

I think you can go for a consistent header style rather than sometimes == foo == and sometimes == foo 😃

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

BTW == title is more idiomatic asciidoc.

This comment has been minimized.

@yloiseau

yloiseau Apr 7, 2015

Contributor

the current golo documentation mainly use == title == 😉

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

Which is bad :-)

Le 7 avr. 2015 à 19:16, Yannick Loiseau notifications@github.com a écrit :

In doc/enums.asciidoc:

+}
+
+function main = |args| {

  • let aString = Option.Some("Hello")
  • println(aString: value())
  • let noString = Option.None()
  • println(noString)
    +}
    +----

+=== Usage example ===
+
+Some well known usages of sum types are:
+
+==== Enumerations
the current golo documentation mainly use == title ==


Reply to this email directly or view it on GitHub.

otherwise func(this: value())
}
function fmap = |this, func| -> match {

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

flatMap

This comment has been minimized.

@yloiseau

yloiseau Apr 7, 2015

Contributor

To match java.util.Optional, bind should be flatMap and fmap should be map. I used here the classical monad vocabulary, but I can switch to Java if you prefer

This comment has been minimized.

@jponge

jponge via email Apr 7, 2015

Member
mv.visitVarInsn(ASTORE, 2);
// java.util.Objects.equals(<member>, other.<member>)
for (String member : value.getMembers()) {

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

Isn't Objects.equals(...) already doing the above checks?

This comment has been minimized.

@yloiseau

yloiseau Apr 7, 2015

Contributor

Objects.equals is used here to compare the members, while the checks above tests for the instance itself

This comment has been minimized.

@jponge

jponge via email Apr 7, 2015

Member
@@ -78,6 +85,14 @@ public String toJVMType() {
return toString().replaceAll("\\.", "/");
}
/**
* @return a JVM reference type representation for this object, e.g.: <code>foo.Bar</code> gives

This comment has been minimized.

@jponge

jponge Apr 7, 2015

Member

I'd rather use {@code } blocks in Javadocs.

This comment has been minimized.

@yloiseau

yloiseau Apr 7, 2015

Contributor

copy-pasted from above 😄

This comment has been minimized.

@jponge

jponge via email Apr 7, 2015

Member
@jponge

This comment has been minimized.

Member

jponge commented Apr 7, 2015

That's a lovely pull request 👏

@danielpetisme

This comment has been minimized.

Contributor

danielpetisme commented Apr 7, 2015

Could it be a good practice to separate all the bytecode generation in dedicated classes? Just as you did w/ JavaBytecodeEnumGenerator.
Currently the class JavaBytecodeGenerationGoloIrVisitor looks like the almighty God of Bytecode generation (up to 800 LoC).
Thoughts?

@jponge

This comment has been minimized.

Member

jponge commented Apr 7, 2015

It is not necessarily very easy to split JavaBytecodeGenerationGoloIrVisitor. We already have a class for structs bytecode generation.

It's big, but quite readable and methods aren't too fat :-)

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 7, 2015

@jponge thanks, it was fun to do...
@danielpetisme I just mimic the struct implementation, without considering refactoring. As @jponge said, the split is not easy since the visitor keeps a global state if I recall correctly.

@artpej

This comment has been minimized.

Contributor

artpej commented Apr 8, 2015

I prefer union to keep a certain consitency with struct and to be less confusing with java enums.

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented Apr 8, 2015

I think I'm gonna rename all stufs as union, to be more clear that it’s more than an equivalent of java enum

@jponge

This comment has been minimized.

Member

jponge commented Apr 8, 2015

I like union 👍

@yloiseau

This comment has been minimized.

Contributor

yloiseau commented May 7, 2015

I think this PR is ready (since highlighting is elsewhere and a lib of useful unions can be done in an other PR).
Any other comments?

Golo allows the definition of sum algebraic data types, also known as
_tagged_union_
footnote:[ http://en.wikipedia.org/wiki/Tagged_union[Wikipedia: Tagged union]

This comment has been minimized.

@jponge

jponge May 7, 2015

Member

Can't we just link instead of a footnote?

This comment has been minimized.

@yloiseau

yloiseau May 7, 2015

Contributor

I have used footnotes everywhere in the doc I wrote (augmentations, decorators)... I'll review that and change to simple links

=== Usage example
Some well known usages of sum types are:

This comment has been minimized.

@jponge

jponge May 7, 2015

Member

I'd suggest

Some well known usages of sum types are the following.

=== JVM existence
An `union` type is compiled to an abstract JVM class. Each alternative value

This comment has been minimized.

@jponge

jponge May 7, 2015

Member

A union type, not an :-)

@jponge

This comment has been minimized.

Member

jponge commented May 7, 2015

Minor comments on the documentation.

Otherwise this is a huge feature, hats-off 😄

@jponge

This comment has been minimized.

Member

jponge commented May 7, 2015

@yloiseau You can merge at your convenience.

yloiseau added some commits Apr 6, 2015

Minor fixes.
Accessibility problem while visiting inner classes to generate bytecode
allowed enum values to be mutable.

Also fixes a formating problem in `ASTEnumValue.toString`
Minor changes for conventions
- titles in asciidoc
- code in javadoc
Rename methods in sample code.
rename `bind` to `flatMap` and `fmap` to `map` to mimic the java
`Optional` API.

yloiseau added a commit that referenced this pull request May 7, 2015

@yloiseau yloiseau merged commit f44a0d8 into eclipse:master May 7, 2015

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

@yloiseau yloiseau deleted the yloiseau:wip/Enum branch May 7, 2015

@yloiseau yloiseau removed their assignment Jun 30, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment