Hacker News new | past | comments | ask | show | jobs | submit login

Oh well I guess I will have to read up on rust some more then before writing long winded posts :) That's very cool.

Does it allow inline type disjunction declarations?

Rather than (never written rust before) something like:

  pub enum ParseResult {
    ParseError(Err) 
    IntResult(Int)
    StringResult(String)
  }
  def parse : ParseResult
Allowing something like this

  newtype ParseError = Err
  def parse : ParseError | Int | String = ...
This avoids having to create specific names for each different type in the disjunction.

I mean we could implement tuples like this

  pub tuple MapEntry<K,V> {
    Key(K)
    Value(V)
  }
  class SortedMap<K,V> {
    def firstEntry() : Option<MapEntry<K,V>>
  }
but everyone probably agrees, that we can figure out that key's are first and values are second, so let's just do this:

  class Map<K,V> {
    def firstEntry() : Option<(K,V)>
  }



No, it doesn't. I think this is the right choice, because when unpacking you need some way to distinguish them anyway, i.e. in

    match foo {
        ParseError(e) => ...
        IntResult(i) => ...
        StringResult(s) => ...
    }
you need something adorning the left to determine what 'e', 'i', and 's' are; you could use the type, but compared to that it doesn't save much typing to just name the branches (which can always be abbreviated), which avoids issues with multiple variants that happen to have the same type.


Yep, the multiple same type issue definitely happens and that compicates client side matching. In my experience it has been infrequent enough that having to make the a couple wrapper classes would be preferable. Sometime tuples can be very ambiguous, take points for example. Point(x:Int,y:Int) is similar to (Int,Int), however sometimes the anonymity is nice so you will want to have both options.

The Boilerplate grows really fast as you try to pass results up a call hierachy.

So let me extend the example to demonstrate it how it doesn't scale:

  //Sorry for the Scala-ness
  //Presume a mapByType partial function on all discriminated unions if the union value is of that type, then it calls
  //the partial function otherwise it just returns whatever it's current value is
  def lexAndParse : ParseError | LexError | Int | String = lex().mapByType{ case t : Token => parse(t) }
  newtype LexError = Err
  def lex() : LexError | Token = ...

  newtype ParseError = Err
  def parse(t : Token) :  ParseError | Int | String = {
      tryParseInt(t).orElse(tryParseString(t)).getOrElse(ParseError("$t not Int Or String"))) }
    }
  def tryParseInt(token : Token) : Option[Int] = ...
  def tryParseString(token : Token) : Option[String] = ...

versus:

  pub enum LexParseResult {
    LexError(Err) 
    ParseError(Err) 
    IntResult(Int)
    StringResult(String)
  }
  def lexAndParse : LexAndParseResult = {
    match lex() {
      LexResult.LexError(e) => LexAndParseResult.LexError(e)
      LexResult.TokenResult(t) => match parse(t) {
         ParseResult.ParseError(e) => LexParseResult.ParseError(e)
         ParseResult.IntResult(e) => LexParseResult.IntResult(e)
         ParseResult.StringResult(e) => LexParseResult.StringResult(e)
      }
    }
  }

  pub enum LexResult {
    LexError(Err) 
    TokenResult(Token)
  }
  def lex : LexResult

  pub enum ParseResult {
    ParseError(Err) 
    IntResult(Int)
    StringResult(String)
  }
  def parse(token : Token) : ParseResult = {
     match lex(){
       Token(t) => 
        tryParseInt(t).map(r=>ParseResult.IntResult(r)).orElse(tryParseString.map(r=>ParseResult.StringResult(r) )).getOrElse(ParseResult.ParseError("$t not Int Or String"))) }
       LexError(e) =>  ParseResult.LexError(e)
    }
  }
  def tryParseInt(token : Token) : Option[Int] = ...
  def tryParseString(token : Token) : Option[String] = ...


To be fair, a similar argument also applies to structs vs. tuples. Unpacking them is really awkward. I suspect language designers only tolerate them because they're so convenient in practice for representing mathematical tuples (where order is actually semantically significant) and have very lightweight syntax in cases where you want to use all or most of the values. But with variants, order is never meaningful and you can only have one disjunctive type at at time (which quashes both those points). There's a Rust RFC which proposes some rather icky syntax for them (match foo { (e|!|!) => ... | (!|i|!) => ... }) and that alone convinced me that this is a no-go.


Order is important in rust variants? That sucks, hmm I hadn't thought about it in this much depth and it's probably obvious from literature seems like there is a whole spectrum of variants then:

1. wrapper variants (where the choosen instance is given a key or it's own wrapper type)

2. ordered variants (where the choosen instance if keyed by it's position)

3. type variants (The only way to differentiate is by type, duplicate types collapse into one type)

I obviously prefer type variants where:

Int | Int | Int simplifies to Int

I think it gives the typechecker the ability to make and understand a much larger variety of useful properties.

For example a method that takes

  def print(x: Num | String)
it will accept Int | String or String | Int or String | Double or String or Int or Double

obviously that isn't totally ideal, you probably rather use polymorphism. (I think I read that the Ceylon implementors thought it would be a big win here then they preferred to use polymorphism, but I could be spreading FUD against my own postion :)

BTW I would argue that type variants have the opposite property of tuples in that as they grow longer that make code much more comprehensible.


Order isn't currently significant; it's the RFC (which I haven't seen) which proposed adding this in some form.


Wow, I just looked over some of them and I think it's awesome that Rust has these RFCs tied to pull requests. Scala has SIPs but they are much less frequently used, and their granularity is much more coarse, yet they are less detailed and commented on by the community.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: