Getting started with Grain by examples

Part 2: OMG! No Class! 😮

Warning: this post is a kind of tip found this morning when I woke up. The sample code I'm going to present may not be good practice, but it will give you an idea of the capabilities of the Grain language.

🖐️ Requirements: read the previous post: k33g.hashnode.dev/getting-started-with-grai..

Seventeen years ago, I was programming in C#, then switched to Java and finally to JavaScript for real in 2005. My biggest surprise was the absence of a class in JavaScript (I even used CoffeeScript to try to compensate). Eventually, I got used to coding without classes, and now even in Java, I don't use inheritance much.

So there is no class in GrainLang, but there are Records 🎉. However, no method can be ported to Records 😢, as it is possible in GoLang, (for example, see: gobyexample.com/methods). In fact, by using the functions (closures), which are types, I achieved my goals (remember: functions are a first-class citizen in GrainLang).

I want a Human structure with a method!

Here is my code snippet:


record Human { 
  mut firstName: String, 
  mut lastName: String, 
  mut sayHello: () -> String // 0️⃣
}

let newHuman = (firstName, lastName) => { // 1️⃣

  // initialise
  let humanInstance = { 
    firstName: firstName, 
    lastName: lastName, 
    sayHello: () => {"default"} 
  } // 2️⃣

  let sayHello = () => {
    "👋 Hello, I am " ++ humanInstance.firstName ++ " " ++ humanInstance.lastName  
  } // 3️⃣
  humanInstance.sayHello = sayHello // 4️⃣

  humanInstance // 5️⃣
}

let jane = newHuman("Jane", "Doe")
let john = newHuman("John", "Doe")

print(jane.sayHello()) // 6️⃣
print(john.sayHello())

jane.firstName = "Janny"
john.firstName = "Johny"

print(jane.sayHello())
print(john.sayHello())
  • 0️⃣: The type of the field is a closure

  • 1️⃣: I use a closure to define a constructor of Human (a kind of factory).

  • 2️⃣ & 3️⃣: I need to initialise the record instance and the sayHello field first. Then, as the record instance exists, I will be able to use a reference to it (humanInstance.firstName and humanInstance.lastName ) inside the closure (3️⃣).

  • 4️⃣: humanInstance.sayHello is mutable so that I can change its value by the new closure.

  • 5️⃣: The constructor returns the record instance

  • 6️⃣: We can use the sayHello method on every Human record instance

Compile and run the code, you will get:

👋 Hello, I am Jane Doe
👋 Hello, I am John Doe
👋 Hello, I am Janny Doe
👋 Hello, I am Johny Doe

That's all for today 👋. As I said, I don't know if it's a good idea to code this (and I'm probably making functional programming purists scream 😭 - sorry for that 🤭), but I like the flexibility of Grain.

See you soon for the next episode 😍.