Eta

A powerful language for building scalable systems on the JVM


Output:
Run

What is Eta?

Eta is a pure, lazy, strongly typed functional programming language on the JVM.

Why Eta?

Purity

Eta is pure by default which means that calling a function with the same arguments will yield the same results every time. This means that you can treat your function definitions as equations and perform substitutions, just like you'd do in math. This makes it easier to understand your code and prevents a lot of bugs that are typical of imperative programming languages. Morever, it encourages a form of programming where you decouple your side effects from your business logic, making it easier to mock tests.

-- Purity allows safe usage of methods like Software Transactional
-- Memory, used to make concurrency safe, easy, and enjoyable.
import Control.Concurrent.STM

type Account = TVar USD
type USD = Double

transferMoney :: USD -> Account -> Account -> IO ()
transferMoney amount sender receiver =
  atomically $ transact amount sender receiver

transact :: USD -> Account -> Account -> STM ()
transact amount sender receiver = do
  senderBalance <- readTVar sender
  receiverBalance <- readTVar receiver
  writeTVar sender (senderBalance - amount)
  writeTVar receiver (receiverBalance + amount)

Laziness

Eta is lazy by default which means that data stays in unevaluated state until a function needs to peek inside. This allows you to program your solution without worrying about whether you've done more computation than necessary. Moreover, you can write multi-pass algorithms in a single-pass by building a lazy data structure and evaluating it as you go.

-- This program is similar to the well-known Unix utility `head`,
-- which prints the first `n` lines of its input.
main :: IO ()
main = do
  -- Extract the first command-line argument.
  ("-n":arg:_) <- getArgs

  -- Convert it to an integer.
  let n = read arg :: Int

  -- Read the contents of standard input lazily.
  contents <- getContents

  -- Process the first `n` lines in a pipeline.
  let newContents = unlines . take n . lines $ contents

  -- Print the result.
  putStr newContents

Type-Safety

A strong type system gives you the power to tell the compiler more information about your code. Eta uses type inference so that it can understand your intent even if you don't explicity specify the types of your expressions. This allows compiler-guided refactoring that will point out all the affected areas due to a given change.

-- Declare a datatype for measurements in Feet and Meters.
newtype Feet = Feet Double
newtype Meters = Meters Double

-- Conversions between measurements.
feet2Meters :: Feet -> Meters
feet2Meters (Feet ft) = Meters $ ft * 0.3048

meters2Feet :: Feet -> Meters
meters2Feet (Meters m) = Feet $ m / 0.3048

-- Feet and Meters have the exact same representation
-- but the compiler will not allow Feet to passed
-- into this function.
volumeOfShippingBox :: Meters -> Meters
volumeOfShippingBox (Meters sideLength) =
  Meters $ sideLength * sideLength * sideLength

Java Interop

Eta runs on the JVM and it is designed to be compatable with Java. This means that you can reuse Java libraries in Eta projects and use Eta modules in Java. Moreover, you can import Java functions in a type-safe way to get benefits like null-safety. Almost all the Java features like inheritance, generics, and so on can be used within Eta making interop very smooth.

-- Type-safe import of a Java method that is null-safe.
foreign import java unsafe "@static java.lang.System.getenv"
  getEnv :: String -> IO (Maybe String)

{- Checks the environment for the HOME environment
variable and prints it out if it exists. -}
main :: IO ()
main = do
  home <- getEnv "HOME"
  case home of
    Just homePath ->
      putStrLn $ "Your home directory is " ++ homePath ++ "!"
    Nothing ->
      putStrLn "Your HOME environment variable is not set"

Concurrency & Parallelism

Eta's immutability makes concurrency a pleasure to work with. Eta offers a wide range of strategies for handling concurrency including the Par monad for fine-grained parallelism and Software Transactional Memory for reusable concurrent programming without locks. The Eta runtime also uses lightweight green threads allowing for highly concurrent web servers. Moreover, the monad design pattern allows programs that are asynchronous by nature can be written in a sequential style.

-- Purity allows safe usage of methods like Software Transactional
-- Memory, used to make concurrency safe, easy, and enjoyable.
import Control.Concurrent.STM

type Account = TVar USD
type USD = Double

transferMoney :: USD -> Account -> Account -> IO ()
transferMoney amount sender receiver =
  atomically $ transact amount sender receiver

transact :: USD -> Account -> Account -> STM ()
transact amount sender receiver = do
  senderBalance <- readTVar sender
  receiverBalance <- readTVar receiver
  writeTVar sender (senderBalance - amount)
  writeTVar receiver (receiverBalance + amount)

Community

Popular ways to connect with the Eta community include mailing lists, chat rooms, and social networks. Please subscribe to the mailing list to receive regular updates about Eta.