As Scala developer learning the IO Monad, and therefore technicalities of Trampolining in general that are necessary for recursion where tail call optimization is not possible, I wonder how Haskell seems to natively avoid it.
I get that Haskell is a lazy language, however I wonder if someone could elaborate a bit further.
For instance, why doesn't ForeverM stackoverflow in scala? Well, I can answer for trampoline, and I can find the actual code that does that in libraries and blogs. I actually implemented a basic trampoline myself to learn.
How does it happens in Haskell? Is there a way to unpack the laziness a bit, give some pointers, and maybe documentation that would help in understanding it better?
sealed trait IO[A] {
.....
def flatMap[B](f: A => IO[B]): IO[B] =
FlatMap[A,B](this, f) // we do not interpret the `flatMap` here, just return it as a value
def map[B](f: A => B): IO[B] =
flatMap[B](f andThen (Return(_)))
}
case class Return[A](a: A) extends IO[A]
case class Suspend[A](resume: () => A) extends IO[A]
case class FlatMap[A,B](sub: IO[A], k: A => IO[B]) extends IO[B]
......
#annotation.tailrec
def run[A](io: IO[A]): A = io match {
case Return(a) => a
case Suspend(r) => r()
case FlatMap(x, f) => x match {
case Return(a) => run(f (a))
case Suspend(r) => run(f( r()))
case FlatMap(y, g) => run(y flatMap (a => g(a) flatMap f))
}
}
Functional programming in general requires tail-call elimination (otherwise the deep chains of function calls overflow the stack). For example, consider this (absurdly inefficient) implementation of an even/odd classifier:
def even(i: Int): Boolean =
if (i == 0) true
else if (i > 0) odd(i - 1)
else odd(i + 1)
def odd(i: Int): Boolean =
if (i == 0) false
else if (i > 0) even(i - 1)
else even(i + 1)
In both even and odd, every branch is either a simple expression (true or false in this case) which doesn't make a function call or a tail-call: the value of the called function is returned without being operated on.
Without tail-call elimination, the (potentially recursive with an indefinite length of a cycle) calls have to be implemented using a stack which consumes memory, because the caller may do something with the result. Tail-call elimination relies on observing that the caller doesn't do anything with the result, therefore the called function can effectively replace the caller on the stack.
Haskell and essentially every other post-Scheme functional language runtime implements generalized tail-call elimination: tail-calls become an unconditional jump (think a GOTO). The famous series of Steele and Sussman papers (the PDFs unfortunately didn't get archived, but you can search for, e.g. AIM-443 (mit or steele or sussman might be required)) known as "Lambda: The Ultimate" (which inspired the name of a programming language forum) goes through the implications of tail-call elimination and how this means that functional programming is actually viable for solving real-world computing problems.
Scala, however, primarily targets the Java Virtual Machine, the specification of which effectively (by design) prohibits generalized tail-call elimination, and the instruction set of which constrains unconditional jumps to not cross the boundaries of a method. In certain limited contexts (basically recursive calls of a method where the compiler can be absolutely sure of what implementation is being called), the Scala compiler performs the tail-call elimination before emitting the Java bytecode (it's theoretically conceivable that Scala Native could perform generalized tail-call elimination, but that would entail some semantic break with JVM and JS Scala (some JavaScript runtimes perform generalized tail-call elimination, though not V8 to my knowledge)). The #tailrec annotation, with which you may have some familiarity, enforces a requirement that the compiler be able to perform tail-call elimination.
Trampolining is a low-level technique at runtime for emulating compile-time tail-call elimination, especially in languages like C or Scala. Since Haskell has performed the tail-call elimination at compile-time, there's thus no need for the complexity of a trampoline (and the requirement to write the high-level code into continuation-passing style).
You can arguably think of the CPU in a Haskell program (or the runtime itself if transpiling to, e.g. JS) as implementing a trampoline.
Trampolining is not the only solution for tail calls. Scala requires trampolining precisely because it runs on the JVM, with the Java runtime. The Scala language developers did not get to choose precisely how their runtime operates, nor their binary format. Because they use the JVM, they must endure every way that the JVM is optimized for Java and not for Scala.
Haskell does not have this limitation, because it has its own runtime, it's own binary format, etc. It can choose precisely how to set up the stack at runtime based on language-level constructs of the Haskell language --- not, of the Java one.
Related
Implicit function types are planned to land in Scala 3.0, and claimed to support "effect capabilities" and composable. It is presented as a better alternative to monad transformers. However, reading further explanation it just seems that implicit function types can only model Reader monad. Am I missing something?
Immediately, it is most trivial to notice that implicit function types allow us to encode the Reader monad primarily allocation free, but losing the convenient for comprehension syntax.
I think what Odersky is referring to when he talks about "a better alternative to monad transformers" is the fact that implicit function types allow you to encode a rather boilerplate free free monad (no pun intended), which is one approach to composing monadic effects.
From the following comment on discourse (emphasis mine):
I guess we both agree that in the future we will have very fine-grained effect systems, so much so that a lot of code with be effectful in some way or another. But then you end up with large parts of your program written in monad structure. The structure itself does not tell you which effects the code has; you need to turn to the types for that.
On the other hand, every time you introduce a new effect category (and it could be as ubiquitous as “not proven to be total”) you need to completely refactor your code into the monadic scheme. And that means you are willing to take a slowdown of (I estimate) 10, risk stackoverflows, be extremely verbose, and have a really hard time composing all your fine grained effects. Or you go free which means better composition but likely even more boilerplate. I can see this work in the sense that you very forcefully tell your users: “don’t use effects, it’s just too painful”. So it could have education value. But if you have to deal with effects, it’s utterly suboptimal in several dimensions.
In his paper, Foundations of Implicit Function Types, Odersky lays out an alternative encoding for free monads using implicit function types which require less boilerplate:
// Free definition
type FreeIFT[A[_], M[_], T] = implicit Natural[A, M] => implicit Monad[M] => M[T]
// GADT defintion
enum KVStoreB[T] {
case Put(key: String, value: Int) extends KVStoreB[Unit]
case Get(key: String) extends KVStoreB[Option[Int]]
}
// Lifted definition
import KVStoreB._
type KVStoreIFT[M[_], T] = FreeIFT[KVStoreB, M, T]
def iftExpr[M[_]]: KVStoreIFT[M, Option[Int]] =
for {
_ <- Put("foo", 2).lift
_ <- Put("bar", 5).lift
n <- Get("foo").lift
} yield n
// Free interpeter
def iftInterpreter = new Natural[KVStoreB, Future] {
def apply[T](fa: KVStoreB[T]): Future[T] = ???
}
// Running the interpreter over the free structure
val iftOutput: Future[Option[Int]] = iftExpr[Future](iftInterpreter)
I know what the monads are and how to use them. What I don't understand is what makes, let's say, Option a monad?
In Haskell a monad Maybe is a monad because it's instantiated from Monad class (which has at least 2 necessary functions return and bind that makes class Monad, indeed, a monad).
But in Scala we've got this:
sealed abstract class Option[+A] extends Product with Serializable { ... }
trait Product extends Any with Equals { ... }
Nothing related to a monad.
If I create my own class in Scala, will it be a monad by default? Why not?
Monad is a concept, an abstract interface if you will, that simply defines a way of composing data.
Option supports composition via flatMap, and that's pretty much everything that is needed to wear the "monad badge".
From a theoretical point of view, it should also:
support a unit operation (return, in Haskell terms) to create a monad out of a bare value, which in case of Option is the Some constructor
respect the monadic laws
but this is not strictly enforced by Scala.
Monads in scala are a much looser concept that in Haskell, and the approach is more practical.
The only thing monads are relevant for, from a language perspective, is the ability of being used in a for-comprehension.
flatMap is a basic requirement, and you can optionally provide map, withFilter and foreach.
However, there's no such thing as strict conformance to a Monad typeclass, like in Haskell.
Here's an example: let's define our own monad.
class MyMonad[A](value: A) {
def map[B](f: A => B) = new MyMonad(f(value))
def flatMap[B](f: A => MyMonad[B]) = f(value)
override def toString = value.toString
}
As you see, we're only implementing map and flatMap (well, and toString as a commodity).
Congratulations, we have a monad! Let's try it out:
scala> for {
a <- new MyMonad(2)
b <- new MyMonad(3)
} yield a + b
// res1: MyMonad[Int] = 5
Nice! We are not doing any filtering, so we don't need to implement withFilter. Also since we're yielding a value, we don't need foreach either. Basically you implement whatever you wish to support, without strict requirements. If you try to filter in a for-comprehension and you haven't implemented withFilter, you'll simply get a compile-time error.
Anything that (partially) implements, through duck-typing, the FilterMonadic trait is considered to be a monad in Scala. This is different than how monads are represented in Haskell, or the Monad typeclass in scalaz. However, in order to benefit of the for comprehension syntactic sugar in Scala, an object has to expose some of the methods defined in the FilterMonadic trait.
Also, in Scala, the equivalent of the Haskell return function is the yield keyword used for producing values out of a for comprehension. The desugaring of yield is a call to the map method of the "monad".
The way I'd put it is that there's an emerging distinction between monads as a design pattern vs. a first-class abstraction. Haskell has the latter, in the form of the Monad type class. But if you have a type that has (or can implement) the monadic operations and obeys the laws, that's a monad as well.
These days you can see monads as a design pattern in Java 8's libraries. The Optional and Stream types in Java 8 come with a static of method that corresponds to Haskell return, and a flatMap method. There is however no Monad type.
Somewhere in between you also have the "duck-typed" approach, as Ionuț G. Stan's answer calls out. C# has this as well—LINQ syntax isn't tied to a specific type, but rather it can be used with any class that implements certain methods.
Scala, per se, does not provide the notion of a monad. You can express a monad as a typeclass but Scala also doesn't provide the notion of a typeclass. But Cats does. So you can create a Monad in Scala with the necessary boiler plate, e.g. traits and implicits cleverly used, or you can use cats which provides a monad trait out of the box. As a comparison, Haskel provides monads as part of the language. Regarding your specific question, an Option can be represented as a monad because it has a flatMap method and a unit method (wrapping a value in a Some or a Future, for example).
Scala and Frege are both typed functional languages that target JVM.
Frege is closer to Haskell, Scala has a more independent history.
But if we don't look at syntactic differences, what are the differences in the allowed programming techniques, styles, concepts between the two?
IMHO both are really good languages but with respect to paradigms, Scala does OO better than Frege but Frege does functional better than Scala. With respect to differences, it comes down to mostly Haskell vs Scala since Frege is (or almost, see differences between Haskell and Frege here) Haskell for the JVM.
Frege's type inference is global so we don't have to annotate types as often as we do in Scala (local inference).
In Frege, modules are just namespaces for types and functions whereas Scala has better module system. http://2013.flatmap.no/spiewak.html
In Frege, functions are curried by default so there is no need for additional constructs for partial function application. Same goes for partial type constructor application.
In Frege, there is no def vs val and everything is function. Hence functions are more first-class than Scala.
Frege has no sub-typing but the type system figures out the sub typing on native calls. For example, you can pass an ArrayList to a function which requires a Java List.
Since there is no subtyping, in Frege we cannot extend a Java class or implement an interface as of now (might be supported in future) so we need to have a Java class which would extend/implement but the method implementations would be passed from Frege as functions.
From Scala, it is easy to call Java but in Frege, a Java class/method must be declared (Just the type and purity annotations) before use. For example, to use Java's LinkedList,
data LinkedList a = native java.util.LinkedList where
native add :: Mutable s (LinkedList a) -> a -> ST s Bool
native get :: Mutable s (LinkedList a) -> Int -> ST s (Maybe a) throws
IndexOutOfBoundsException
native new :: () -> STMutable s (LinkedList a)
Here since the functions mutate the object, they must be in ST monad. Also note that here Frege also handles null returned from the get method since it is annotated with Maybe type. The only way null can get through to your Frege program is through native interface since Frege doesn't have a notion of null.
Another example:
pure native floor Math.floor :: Double -> Double
which states that the function is pure and hence the signature directly reflects the original Java signature without IO or ST.
Frege has no variables as in Scala's var and the side effects
are more explicit through types. (Just no null, no var and explicit side effects make Frege more interesting, atleast for me. In a sense, Frege, just as Haskell, is a "fine imperative programming language", for the JVM!)
Being a Haskell dialect, Frege is more natural towards Functors, Applicatives, Monads and other functional "patterns" and has those in it's standard library whereas in Scala, you might need Scalaz.
Frege is lazy by default but strictness can be enabled where necessary through ! whereas Scala is strict by default but has lazy keyword for lazy evaluation.
Nevertheless, being JVM languages, one language can benefit from other. I once ported an Akka example to Frege. In the end, it comes down to strictness, purity, functional, OO and type inference and how much they matter to you.
Apart from syntactical issues, the biggest difference is in the type system and the execution model.
As #senia already pointed out, scala is strict and not pure, which does not mean that you can't write pure functions (you can do that in C, too), just that the compiler won't enforce it.
Frege, OTOH is lazy and pure, which means that all impure effects are forced to live in the ST or IO monad. The type system is essential that of Haskell 2010, with type classes and additional higher rank function types. Type inference works program wide, the only exception are functions with higher rank types, where at least the polymorphic argument must be annotated. Here is an example:
both f xs ys = (f xs, f ys)
For this function, the compiler infers the type:
both :: (α->β) -> α -> α -> (β, β)
Note that both xsand ysget the same type, because of the application of f.
But now lets say we want use a polymorphic list function that we can use with differently typed xs and ys. For example, we want to write:
both reverse [1,2,3] ['a' .. 'z']
As it stands, this application would be in error, because the two lists have different element types and hence different types. So the compiler would refuse the character list.
Fortunately, we can tell the compiler more precisly what we want with a type annotation:
both :: (forall e.[e] -> [e]) -> [a] -> [b] -> ([a], [b])
This tells the following: we will pass to both a function that does some list transformation but doesn't care about the list element type. Then we pass 2 lists with possibly different element types. And we get a tuple with our transformed lists back.
Note that the code of both needs not to be changed.
Another way to achieve the same would be to write:
both (f :: forall e.[e]->[e]) xs ys = (f xs, f ys)
and the type checker infers the rest, namely that xs and ys must be lists, but can have different element types.
Scalas type system fully (to my knowledge) supports OO. While Frege supports it only partially with regard to types imported for Java, but does not support definition of own OO-like types.
Hence, both languages support functional programming in a JVM environment, although in completly different niches. A third niche is the dynamically typed one, where Clojure is king in the JVM world.
Maybe it's off topic, but Scala can be used to develop Android applications (*), but Frege hasn't been successfully used for that, yet.(**) IIRC, it's because interop with existing Java libraries is much easier in Scala than in Frege, among other issues.
(*) Caveat emptor. I've only done small, example programs myself.
(**) Frege/Java mixes have been used for Android applications, but Frege-only applications are still not available, AFAIK.
I like the use of require, assert, assume, and ensuring to document pre-conditions and post-conditions of a given function, but I have also come to use scalaz.Validation mostly to perform checks that require used to do. But, lately, I have been most interested in using PartialFunction, possibly with Validation, to document a pre-condition. Something like:
case class Summary(user: String, at: DateTime, ....)
val restricted_add: PartialFunction[(Summary, Summary), Summary] = {
case (s1: Summary, s2: Summary) if s1.user != s2.user && s1.at == s2.at =>
}
with a utility function for capturing and converting a match error into a validation failure (e.g. restricted_add.lift(_).toSucess("Cannot add.")). Somehow the case clause above seems less noisy than a For with scalaz.Validation. But I feel like I am going against the language. I'm not using def's natural syntax for defining parameters, every client of such a function would have to use a utility function when invoking it, I would be capturing exceptions and converting them into validations, instead of working only with validations or only with exceptions.
What seems like the least noisy, most functional way of documenting these pre-conditions through code? Validation returning functions, require statements, PartialFunction, other...?
As #RandallSchulz already stated, implicitly documenting preconditions in code only serves for your private/interal methods, never for your public API. If you're writing public methods, you should also document the preconditions in the methods docs and maybe the usage examples, if they're counter-intuitive.
That being said, in my opinion the least noisy and the most functional way are not the same in Scala. This is because require cannot be considered functional, since it throws an exception if the condition is not met, which would be catched somewhere, changing the control flow and thus being a side effect.
Nonetheless, I think that require is by far the least noisy way to specify a preconditions:
def non_func(x:Int, y:Int):Int = {
require(x >= 0 && y >= 0)
x + y
}
I'm not entirely sure about the most functional way to check preconditions, but I think that using pattern matching and returning an Option could be considered functional, but noisier than require:
def func(x:Int, y:Int):Option[Int] = (x,y) match {
case (x, y) if x >= 0 && y >= 0 => Some(x + y)
case _ => None
}
The advantage of using an Option is that you can flatMap that shit (scnr):
val xs = List((1,2), (0,0), (-1,0), (-1,-1), (3,4))
xs.flatMap(x => func(x._1, x._2)) # => List(3, 0, 7)
The alone is probably a good enough reason to add just a little more noise compared to require since it enables you to use a lot of the standard Scala library more concisely.
Regarding partial functions, you are basically answering your own question, since you say that its more cumbersome to use and to define. I don't have any experience with Scalaz though, so I can't really compare the "pure scala" approach to anything one might do with Scalaz.
This is just one of those "I was wondering..." questions.
Scala has immutable data structures and (optional) lazy vals etc.
How close can a Scala program be to one that is fully pure (in a functional programming sense) and fully lazy (or as Ingo points out, can it be sufficiently non-strict)? What values are unavoidably mutable and what evaluation unavoidably greedy?
Regarding lazyness - currently, passing a parameter to a method is by default strict:
def square(a: Int) = a * a
but you use call-by-name parameters:
def square(a: =>Int) = a * a
but this is not lazy in the sense that it computes the value only once when needed:
scala> square({println("calculating");5})
calculating
calculating
res0: Int = 25
There's been some work into adding lazy method parameters, but it hasn't been integrated yet (the below declaration should print "calculating" from above only once):
def square(lazy a: Int) = a * a
This is one piece that is missing, although you could simulate it with a local lazy val:
def square(ap: =>Int) = {
lazy val a = ap
a * a
}
Regarding mutability - there is nothing holding you back from writing immutable data structures and avoid mutation. You can do this in Java or C as well. In fact, some immutable data structures rely on the lazy primitive to achieve better complexity bounds, but the lazy primitive can be simulated in other languages as well - at the cost of extra syntax and boilerplate.
You can always write immutable data structures, lazy computations and fully pure programs in Scala. The problem is that the Scala programming model allows writing non pure programs as well, so the type checker can't always infer some properties of the program (such as purity) which it could infer given that the programming model was more restrictive.
For example, in a language with pure expressions the a * a in the call-by-name definition above (a: =>Int) could be optimized to evaluate a only once, regardless of the call-by-name semantics. If the language allows side-effects, then such an optimization is not always applicable.
Scala can be as pure and lazy as you like, but a) the compiler won't keep you honest with regards to purity and b) it will take a little extra work to make it lazy. There's nothing too profound about this; you can even write lazy and pure Java code if you really want to (see here if you dare; achieving laziness in Java requires eye-bleeding amounts of nested anonymous inner classes).
Purity
Whereas Haskell tracks impurities via the type system, Scala has chosen not to go that route, and it's difficult to tack that sort of thing on when you haven't made it a goal from the beginning (and also when interoperability with a thoroughly impure language like Java is a major goal of the language).
That said, some believe it's possible and worthwhile to make the effort to document effects in Scala's type system. But I think purity in Scala is best treated as a matter of self-discipline, and you must be perpetually skeptical about the supposed purity of third-party code.
Laziness
Haskell is lazy by default but can be made stricter with some annotations sprinkled in your code... Scala is the opposite: strict by default but with the lazy keyword and by-name parameters you can make it as lazy as you like.
Feel free to keep things immutable. On the other hand, there's no side effect tracking, so you can't enforce or verify it.
As for non-strictness, here's the deal... First, if you choose to go completely non-strict, you'll be forsaking all of Scala's classes. Even Scalaz is not non-strict for the most part. If you are willing to build everything yourself, you can make your methods non-strict and your values lazy.
Next, I wonder if implicit parameters can be non-strict or not, or what would be the consequences of making them non-strict. I don't see a problem, but I could be wrong.
But, most problematic of all, function parameters are strict, and so are closures parameters.
So, while it is theoretically possible to go fully non-strict, it will be incredibly inconvenient.