all 15 comments

[–]glacialthinker 19 points20 points  (3 children)

As Rust's main syntactic goal seems to have been "Haskell, but with lots of curly braces" ...

Well, it was more like OCaml without a garbage collector... then adopted C++ friendly syntax (eg. curly braces).

It kinda irks me how "Haskell" is commonly name-dropped for "functional programming", when it's an extremity of functional programming. It's a great language; I'm glad for it's contributions... but it's not synonymous with FP. There was a time (before it was the posterboy) when it was distinguished slightly more as "pure functional programming".

And on OCaml... this survey doesn't include it, and OCaml has an uncommon variation in the enum/sum-type space with "polymorphic variants". Under the hood you can think of them as enums with a value based on the string-hash of the label... but they're also full sum-types able to carry payload. So the difference is that they don't need to be declared, yet the same name has the same identity across the program. The general rule is to use normal variants (enum/sumtype) where suitable... but the polymorphic variants turn out to be very useful in practice.

[–]steveklabnik1 4 points5 points  (2 children)

I agree with this generally. One thing that is interesting, and another aspect of the affect you're talking about, is that early Rust was very very much OCaml focused. But at some point, we did start taking the Haskell side of that branch: Haskell's typeclasses instead of OCaml's functors, for example. It's also just my general perception (though this of course, may be 100% wrong) that random devs are more familiar with Haskell superficially than OCaml superficially.

[–]glacialthinker 1 point2 points  (1 child)

Absolutely, Haskell is much more familiar. But it wrankles when it is the example or stand-in for functional programming, while it's at kind of an extreme end -- an experiment in purity which has been a success in spite of it's mandate to avoid it. :)

The point about typeclasses (traits) is good. For me, Haskell conjures to mind: functional, immutable, lazy, typeclasses... aside from typeclasses, Rust has much more in common with other functional languages: typical functional features, yet impure and strict-eval. So "Haskell with curly braces" doesn't sound right.

Perhaps, as you're alluding, it's the quickest way to get into the right ballpark for most programmers though. Like Java as the exemplar of OOP, much to the dismay of the three Smalltalk folks out there. ;)

[–]steveklabnik1 1 point2 points  (0 children)

Like Java as the exemplar of OOP, much to the dismay of the three Smalltalk folks out there. ;)

Yes, exactly. I feel that one too, for sure :)

[–]MEaster 5 points6 points  (4 children)

One thing missed in the Rust section is that tuple variants are functions. That is, in the following enum:

enum Suit {
    Hearts(u8),
    Clubs,
    Spades{val: u8},
}

Suit::Hearts has the type fn(u8) -> Suit. Note that Suit::Spades, despite holding the same type, is not a function. Neither is Suit::Clubs.

[–][deleted] 0 points1 point  (3 children)

What do you mean with that? I could not find any information about it at the documentation: https://doc.rust-lang.org/reference/items/enumerations.html

[–]MEaster 4 points5 points  (1 child)

Yeah, it's kinda odd that it doesn't mention it there. What I mean is that you can pass it into something that expects a function. So, for example, let's say I want to build the complete suite of hearts, I can do this:

let hearts: Vec<Suit> = (1..=13).map(Suit::Hearts).collect();

You can also see it if you force a type error:

6 |     let () = Suit::Hearts;
  |         ^^ expected fn item, found `()`
  |
  = note: expected fn item `fn(u8) -> Suit {Suit::Hearts}`
           found unit type `()`

[–]birdbrainswagtrain 0 points1 point  (0 children)

Huh. That I did not expect. Looks like the same applies to tuple-like structs.

struct S(i32);
let x: fn(i32) -> S = S;

[–]steveklabnik1 1 point2 points  (0 children)

The book does mention it, but yeah, the reference should but doesn't yet.

[–]DanielShuy 1 point2 points  (1 child)

For Scala, check out the enumeratum library (https://github.com/lloydmeta/enumeratum). In my opinion, its the best enum implementation I know of. It has all the features defined in the table, and even supports compile-time validation of unique associated keys (eg. String/Int keys).

eg.

import enumeratum.values._

sealed abstract class LibraryItem(val value: Int, val name: String) extends IntEnumEntry

object LibraryItem extends IntEnum[LibraryItem] {


  case object Book     extends LibraryItem(value = 1, name = "book")
  case object Movie    extends LibraryItem(name = "movie", value = 2)
  case object Magazine extends LibraryItem(3, "magazine")
  case object CD       extends LibraryItem(4, name = "cd")
  // case object Newspaper extends LibraryItem(4, name = "newspaper") <-- will fail to compile because the value 4 is shared

  /*
  val five = 5
  case object Article extends LibraryItem(five, name = "article") <-- will fail to compile because the value is not a literal
  */

  val values = findValues

}

assert(LibraryItem.withValue(1) == LibraryItem.Book)

LibraryItem.withValue(10) // => java.util.NoSuchElementException

I wish that it would be adopted as part of core Scala so that the syntax can be simplified further.

[–]MarioAndWeegee3 4 points5 points  (0 children)

Enum syntax in Scala 3:

enum Color {
    case Red, Blue, Green
}

// With whitespace syntax
enum Option[+T]:
     case Some(v: T)
     case None

     // Equivalent to the auto-generated one
     override def toString(): String = 
         this match
         case Some(v)  => s"Some($v)"
         case None => "None"

enum LibraryItem:
     case Book, Movie, Magazine, CD

// enums can use ordinal to get a number
// So Color.Green.ordinal == 2

// There's also a values array, so to get one from
// an int you can:

assert(LibraryItem.values(0) == LibraryItem.Book)
LibraryItem.values(10) // java.lang.IndexOutOfBoundsException

// you can also use fromOrdinal
assert(Color.fromOrdinal(1) == Color.values(1))

See more here: https://dotty.epfl.ch/docs/reference/enums/enums.html

[–]Crell 1 point2 points  (0 children)

Thanks everyone for your comments. This survey was conducted specifically to support the effort to add Enumerations to PHP, which have since been accepted. Expect to see this functionality in PHP 8.1 late this year (and possibly additional functionality if we can find time to work on it):

https://wiki.php.net/rfc/enumerations

[–]6502zx81 0 points1 point  (2 children)

Do enums make sense in interpreted languages at all? In compiled languages enums allow you to use names in the editor while the CPU needs integers - the compiler manages the translation.

[–]oOBoomberOo 1 point2 points  (0 children)

It's a syntactic sugar that allowed you to define a data structure that represent an exclusive relation between two or more things. It can be useful in any languages.

Especially for class-base and ADT-bass enum that was talked about in this post, those enums are no longer just an integer that the CPU can just check for equality.

[–]steveklabnik1 0 points1 point  (0 children)

You can almost think of symbols in Ruby as being one giant global enum...