I'm starting from an assumption that keeping track of effects is necessary, and that these effects are things that aren't built into the language. E.g. database access, structured logging (like an audit trail). (You're allowed to say that you wouldn't want to carefully keep track of these things, that it doesn't matter whether a particular function accesses the database or not, but in that case rather than a repetitive code cost, I'd say you end up with a testing/safety/maintenance cost). An example is necessarily pretty abstract or complex, because it's only when you're doing these complex things that you really need to abstract over them at multiple levels. But I'll try:
One of the fun things I can do in Scala is build up an "action" using for/yield syntax:
for {
f ← auditedFunction1()
g = nonAuditedFunction(f)
h ← auditedFunction2(g)
} yield h
And then I can have my Spray directives know how to handle a generic action, by writing a simple instance for each action. E.g. I can tell it to display the audit trail if a particular header is present:
implicit def auditOnSuccessFutureMagnet[T](
inner: Auditable[T]) =
new OnSuccessFutureMagnet {
type Out = AuditTrail \/ T
def get = header(MySpecialHeader) map {
case Some("mySpecialValue") =>
-\/(inner.written)
case None => \/-(inner.value)
}
And I can do a similar thing for database actions, wrapping them in a transaction at the HTTP request level - giving me the equivalent of session-creation-in-view, but tied to the actual request rather than a thread, and with safety maintained by the type system.
All of this stuff is typesafe. I can only use a contextual action that I can provide an implementation for how to handle (there are a few defaults like e.g. Future), and I can only return an object "inside" the context that I've also provided how to handle (e.g. by providing a json formatter for that type). And none of this is special or built-in (other than the OnSuccessFutureMagnet interface that I'm implementing); all these things are custom classes or from different libraries.
How could I do that in Go? I'd have to declare each of the different combinations (each possible DatabaseAction[A] and also each possible Auditable[A]) as separate types, right? And my framework certainly couldn't offer the generic handling of F[A] that lets me reuse the same functions for both, so I'd have to have separate copies for anything that I wanted to work with both DatabaseAction and Auditable.
One of the fun things I can do in Scala is build up an "action" using for/yield syntax:
I can do the same thing for database actions: And then I can have my Spray directives know how to handle a generic action, by writing a simple instance for each action. E.g. I can tell it to display the audit trail if a particular header is present: And I can do a similar thing for database actions, wrapping them in a transaction at the HTTP request level - giving me the equivalent of session-creation-in-view, but tied to the actual request rather than a thread, and with safety maintained by the type system.All of this stuff is typesafe. I can only use a contextual action that I can provide an implementation for how to handle (there are a few defaults like e.g. Future), and I can only return an object "inside" the context that I've also provided how to handle (e.g. by providing a json formatter for that type). And none of this is special or built-in (other than the OnSuccessFutureMagnet interface that I'm implementing); all these things are custom classes or from different libraries.
How could I do that in Go? I'd have to declare each of the different combinations (each possible DatabaseAction[A] and also each possible Auditable[A]) as separate types, right? And my framework certainly couldn't offer the generic handling of F[A] that lets me reuse the same functions for both, so I'd have to have separate copies for anything that I wanted to work with both DatabaseAction and Auditable.