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

I've been using Monads in Scala for not throwing wild exceptions, but still being able to stop the computation immediately if needed. For example you want to validate things with a Validation[A] type, which can either be Valid or Invalid. The binding function in Scala is called flatMap, so flatMap for a Valid value returns a lambda having the included value as the parameter and Invalid doesn't have a function call, so the flatMap operation stops.

Example:

  Valid("userId").flatMap(userId =>
    Invalid("address").map(address => User(id = userId, address = address))
would stop for the address validation, because the flatMap for a Validation is specified as:

  def flatMap[B](f: A => Validation[B]): Validation[B] = this match {
    case Valid(value) => f(value)
    case Invalid(value) => Invalid(value)
  }
and map for a Validation is specified as:

  def map[B](f: A => B): Validation[B] = this match {
    case Valid(value) => Valid(f(value))
    case Invalid(value) => Invalid(value)
  end
And of course we can have a bit of syntactic sugar to not nest dozens of flatmaps, in Scala we use for:

  for {
    userId <- Valid("userId")
    address <- Invalid("address")
  } yield {
    User(id = userId, address = address)
  }
Now in the main function we can handle the Valid and Invalid with pattern matching and look, we can stop the computation without throwing exceptions, which makes testing and everything way simpler.



While I really appreciate your cleverness, I would argue that this is an example of so-called overengineering or, an analogy from architecture - redundant decorations. Why do I need all this complications instead of a predicate?

OK, in some statically typed languages which perform type-inference, there is a restrictions for homogeneity of conditionals and aggregates, otherwise all your type inference falls apart. So, to address this problem we could use data simple structures, like tuples, or we could create a new data-type. The canonical example that Nothing or Just T type. Because different branches of a conditional must be of the same type - this is the type. Also for the sake of type-consistency, it is parametrized type. In old-school languages we will just return nil, or (values ....) Semantically there is no difference.

Ideally, types and semantics should not interfere. Complicating semantics in order to satisfy a type-system is a controversial idea.

As for Monads - it is just an Abstract Data Type, nothing special, in which Semantics and Type information complement each other. It has been created to keep types consistent - a parameteized type along with two procedures.

As an old-school programmer, I used to think about types as tags, the way it is in Lisps. (Of course, I know that these tags could be arranged in categories, hierarchies, and so-called "laws" could be defined). So, in my view, this is nothing but nested type-tags. It makes it easy to view Semantics and Types separately.

In Haskell a Monad has another "function", which is, in my opinion, is the reason why it was created. Along with satisfying the type-system, it also ensures an order of evaluation in a lazy language. The semantics is obvious - you evaluate an expression, and lift (type-tag) the value back into Monad, so the whole expression has type Monad T.

OK, this parametrized type is justified in Haskell, but in other languages, in my opinion, it is a redundant decoration, not a necessity.




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

Search: