Understand Go in 5 minutes, in Haskell
See also: youtube video (french) - peertube video (french) - version française
Go and Haskell are rather opposite: Go favors simplicity, Haskell favors expressiveness. However, these two programming languages are not so different. More precisely, the basic concepts in Go are also available in Haskell, and quite similarly.
This post translates, in Haskell, the code samples presented in this article. The objective is not to detail Go and Haskell, nor to demonstrate that one is better than the other, but only to illustrate equivalent Haskell code to some Go code.
Hello world
Let’s start with a classic “helloworld”. In Go, we have to define a package, import the fmt
module and define a main
function.
Or more simply:
In Haskell, we also have to define a main
function, but it’s not mandatory to define a module.
Functions
Go has functions. The syntax for defining or evaluating a function is quite similar to any C-like language.
package main
import "fmt"
func add(x int, y int) int { // define a function add
return x + y
}
func main() {
var i, j int = 10, 2
fmt.Println(add(i, j)) // evaluate add
}
In Haskell, functions are defined using the curried form. Function evaluation needs no parenthesis.
add :: Int -> Int -> Int -- define a function add
add x y = x + y
main :: IO ()
main = do
let i = 10
j = 2
print (add i j) -- evaluate add
Go can use the curried form too (see this comment).
package main
func main() {
var add = func(a int) func(b int) int {
return func(b int) int {
return a + b
}
}
var x = 10
var y = 2
println(add(x)(y))
}
Interfaces
Go can define types and interfaces. For example, we can define a GoDeveloper
type that implements a Developer
interface.
package main
import "fmt"
type Developer interface { // define an interface Developer
Code() string
}
type GoDeveloper struct { // define a type GoDeveloper
}
func (g GoDeveloper ) Code() string { // instantiate Developer for GoDeveloper
return "Go code"
}
func main() {
goDeveloper := GoDeveloper{}
fmt.Println(goDeveloper.Code())
}
The Haskell type system is very powerful but the previous code can be implemented quite simply, using type classes and algebraic data types.
class Developer a where -- define an "interface" Developer
code :: a -> String
data GoDeveloper = GoDeveloper -- define a type GoDeveloper
instance Developer GoDeveloper where -- instantiate Developer for GoDeveloper
code g = "go code"
main :: IO ()
main = do
let goDeveloper = GoDeveloper
putStrLn (code goDeveloper)
Lightweight processes
Finally, Go has lightweight processes, called “go-routines”. A go-routine is basically a function called asynchronously using the keyword go
.
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world") // launch a lightweight process asynchronously
say("hello")
}
Haskell also has lightweight processes, launched using forkIO
.
import Control.Concurrent
import Control.Monad
import System.Clock
say :: String -> IO ()
say s = forM_ [1 .. 5] $ \_ -> do
threadDelay 100000
putStrLn s
main :: IO ()
main = do
forkIO (say "world") -- launch a lightweight process asynchronously
say "hello"
Conclusion
The Go language can define and use functions, types, interfaces and lightweight processes ("go-routines). These features also exist in Haskell and can be used quite similarly.