Using Eta for what you don't like writing in Scala

Using Eta for what you don't like writing in Scala

The integration of Scala with a lazy-by-default, modern and statically-typed functional language could lead to some interesting language-heterogeneous implementations despite the fact that the Eta programming language is missing a comprehensive documentation, robust concurrency support and an interactive REPL.

This talk aims to cover some of those interaction patterns to help both people already working or struggling with Haskell, and through that to also motivate the use of Eta for those still unfamiliar with this "end of the FP spectrum".

2fa9df92d8d7eba3e17207c86f953be3?s=128

Filippo Vitale

May 03, 2017
Tweet

Transcript

  1. 2.
  2. 3.
  3. 4.
  4. 5.
  5. 6.
  6. 10.

    Modern Haskell on the JVM - Rahul Muttineni - github.com/typelead/eta

    - github.com/typelead/etlas - github.com/typelead/eta-hackage - Objectives - JVM + Haskell Ecosystems - Accessibility for beginners It is GHC 7.10.3
  7. 11.

    Modern Haskell on the JVM - Rahul Muttineni - github.com/typelead/eta

    - github.com/typelead/etlas - github.com/typelead/eta-hackage - Objectives - JVM + Haskell Ecosystems - Accessibility for beginners forked from haskell/cabal It is GHC 7.10.3
  8. 12.

    Modern Haskell on the JVM - Rahul Muttineni - github.com/typelead/eta

    - github.com/typelead/etlas - github.com/typelead/eta-hackage - Objectives - JVM + Haskell Ecosystems - Accessibility for beginners forked from haskell/cabal set of patches for particular packages from Hackage that cannot be built out-of-the-box It is GHC 7.10.3
  9. 14.

    module Main where import System.CPUTime main :: IO () main

    = getCPUTime >>= print object Main extends App { println(System.nanoTime() * 1000) } https://github.com/filippovitale/eta-playground/tree/master/simplest-ghc-program
  10. 15.

    module Main where import System.CPUTime main :: IO () main

    = getCPUTime >>= print object Main extends App { println(System.nanoTime() * 1000) } λ> :t getCPUTime getCPUTime :: IO Integer
  11. 16.

    module Main where import System.CPUTime main :: IO () main

    = getCPUTime >>= print object Main extends App { println(System.nanoTime() * 1000) } λ> :t print print :: Show a => a -> IO () λ> :t getCPUTime getCPUTime :: IO Integer λ> :i Integer … instance Show Integer
  12. 17.

    module Main where import System.CPUTime main :: IO () main

    = getCPUTime >>= print object Main extends App { println(System.nanoTime() * 1000) }
  13. 18.

    module Main where import System.CPUTime main :: IO () main

    = getCPUTime >>= print object Main extends App { nanoTime() * 1000 |> println }
  14. 20.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ...
  15. 21.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... $ java -jar RunMain.jar 422227000000
  16. 22.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... Driver .hs ghc Core STG https://www.microsoft.com/en-us/research/wp-content/uploads/1992/04/spineless-tagless-gmachine.pdf
  17. 23.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... Driver Parser Typechecker Desugarer Optimizer Simplifier Code Generator .hs Core STG ghc http://www.aosabook.org/en/ghc.html
  18. 24.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... Driver Parser Typechecker Desugarer Optimizer Simplifier Code Generator .hs Core STG .jar
  19. 25.

    $ cat > Main.hs $ eta Main.hs [1 of 1]

    Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... $ ls -lh -rw-r--r-- 711B Main.hi -rw-r--r-- 84B Main.hs -rw-r--r-- 3.3K Main.jar -rw-r--r-- 18M RunMain.jar - Main.jar content - all the dependencies - GHC runtime-system https://github.com/filippovitale/eta-playground/tree/master/simplest-ghc-program
  20. 27.
  21. 28.

    {-# LANGUAGE MagicHash #-} module Main where import Java --

    foreign import declaration foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeIO :: IO Int64 main :: IO () main = nanoTimeIO >>= print $ eta Main.hs && java -jar RunMain.jar [1 of 1] Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... 149320110727531 IO Monad https://github.com/filippovitale/eta-playground/blob/master/java-static-import
  22. 29.

    {-# LANGUAGE MagicHash #-} module Main where import Java --

    foreign import declaration foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeJava :: Java a Int64 main :: IO () main = java nanoTimeJava >>= print $ eta Main.hs && java -jar RunMain.jar [1 of 1] Compiling Main ( Main.hs, Main.jar ) Linking RunMain.jar ... 149319575014401 java :: Java c a -> IO a Java Monad
  23. 30.

    {-# LANGUAGE MagicHash #-} module Main where import Java --

    foreign import declarations foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeJava :: Java a Int64 foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeIO :: IO Int64 forall a. Java a b -- isomorphic to -- IO b https://github.com/typelead/eta/issues/255#issuecomment-290937594
  24. 31.

    {-# LANGUAGE MagicHash #-} module Main where import Java --

    foreign import declarations foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeJava :: Java a Int64 foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeIO :: IO Int64 foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimePlain :: Int64 main :: IO () main = do java nanoTimeJava >>= print java nanoTimeJava >>= print nanoTimeIO >>= print nanoTimeIO >>= print print nanoTimePlain print nanoTimePlain
  25. 32.

    {-# LANGUAGE MagicHash #-} module Main where import Java --

    foreign import declarations foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeJava :: Java a Int64 foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimeIO :: IO Int64 foreign import java unsafe "@static java.lang.System.nanoTime" nanoTimePlain :: Int64 main :: IO () main = do java nanoTimeJava >>= print java nanoTimeJava >>= print nanoTimeIO >>= print nanoTimeIO >>= print print nanoTimePlain print nanoTimePlain $ java -jar RunMain.jar 149319575014401 149320109787317 149320110727531 149320111517553 149320112027191 149320112027191 https://github.com/filippovitale/eta-playground/blob/master/java-static-import
  26. 35.

    $ mkdir javafx-canvas-grid && cd "$_" $ etlas init &&

    tree . ├── LICENSE ├── Setup.hs ├── javafx-canvas-grid.cabal
  27. 36.

    $ mkdir javafx-canvas-grid && cd "$_" $ etlas init &&

    tree . ├── LICENSE ├── Setup.hs ├── javafx-canvas-grid.cabal └── src ├── CanvasGridApp.hs ├── JavaFX │ ├── │ ├── │ └── ├── JavaFX.hs └── Main.hs
  28. 37.

    $ mkdir javafx-canvas-grid && cd "$_" $ etlas init &&

    tree . ├── LICENSE ├── Setup.hs ├── javafx-canvas-grid.cabal └── src ├── CanvasGridApp.hs ├── JavaFX │ ├── Core.hs │ ├── Methods.hs │ └── Types.hs ├── JavaFX.hs └── Main.hs
  29. 38.

    module JavaFX.Types where import Java data {-# CLASS "javafx.stage.Stage" #-}

    Stage = Stage (Object# Stage) deriving Class data {-# CLASS "javafx.scene.Scene" #-} Scene = Scene (Object# Scene) deriving Class data {-# CLASS "javafx.scene.Group" #-} Group = Group (Object# Group) deriving Class -- There is a default instance for “object arrays” data {-# CLASS "javafx.scene.canvas.Canvas" #-} Canvas = Canvas (Object# Canvas) deriving Class data {-# CLASS "javafx.scene.canvas.Canvas[]" #-} Canvases = Canvases (Object# Canvases) der... instance JArray Canvas Canvases src/JavaFX/Types.hs
  30. 39.

    module JavaFX.Methods where import Java import JavaFX.Types -- Stage methods

    foreign import java unsafe "show" show :: Java Stage () foreign import java unsafe "setTitle" setTitle :: String -> Java Stage () foreign import java unsafe "setScene" setScene :: Scene -> Java Stage () -- GraphicsContext methods foreign import java unsafe "fillOval" fillOval :: Double -> Double -> Double -> Double -> Java GraphicsContext () src/JavaFX/Methods.hs
  31. 41.

    module CanvasGridApp where import Java data {-# CLASS "org.eta.CanvasGridApp extends

    javafx.application.Application" #-} CanvasGridApp = CanvasGridApp (Object# CanvasGridApp) start :: Stage -> Java CanvasGridApp () start primaryStage = undefined foreign export java "start" start :: Stage -> Java CanvasGridApp () src/CanvasGridApp.hs class CanvasGridApp extends Application { override def start(primaryStage: Stage): Unit = { ??? } }
  32. 42.

    start :: Stage -> Java CanvasGridApp () start primaryStage =

    do c <- newCanvas w h gc <- c <.> getGraphicsContext2D src/CanvasGridApp.hs -- Execute a Java action in the Java monad of another class -- with respect to the given object. (<.>) :: (Class c) => c -> Java c a -> Java b a
  33. 43.

    start :: Stage -> Java CanvasGridApp () start primaryStage =

    do c <- newCanvas w h gc <- c <.> getGraphicsContext2D forM_ [(i, j) | i <- [0..(n-1)], j <- [0..(n-1)]] $ \(i, j) -> gc <.> fillOval (u*i) (u*j) (u*r) (u*r) src/CanvasGridApp.hs
  34. 44.

    start :: Stage -> Java CanvasGridApp () start primaryStage =

    do c <- newCanvas w h gc <- c <.> getGraphicsContext2D forM_ [(i, j) | i <- [0..(n-1)], j <- [0..(n-1)]] $ \(i, j) -> gc <.> fillOval (u*i) (u*j) (u*r) (u*r) s <- (newGroup c) >>= newScene primaryStage <.> (setScene s >> setTitle "Eta-JavaFX CanvasGrid" >> show) where w = 320 h = 320 n = 8 u = (min w h) / n r = 0.9 src/CanvasGridApp.hs
  35. 45.

    $ etlas run Resolving dependencies... Configuring javafx-canvas-grid-0.1.0.0... Preprocessing executable 'javafx-canvas-grid'

    Building executable 'javafx-canvas-grid' [1 of 6] Compiling JavaFX.Types [2 of 6] Compiling JavaFX.Methods [3 of 6] Compiling JavaFX.Core [4 of 6] Compiling JavaFX [5 of 6] Compiling CanvasGridApp [6 of 6] Compiling Main Linking dist/build/javafx-canvas-grid/javafx-canvas-grid.jar ... Running javafx-canvas-grid... https://github.com/filippovitale/eta-playground/tree/master/javafx-canvas-grid
  36. 50.

    start :: Stage -> Java TeaStormApp () start primaryStage =

    do t0 <- nanoTime c <- newCanvas w h s <- (newGroup c) >>= newScene loop <- newAnimationLoop c t0 n u m loop <.> startAnimation primaryStage <.> (setTitle "Eta-JavaFX TeaStorm" >> setScene s >> show) where n = 96 m = 10 w = n * m h = n * m u = (min w h) / n src/TeaStormApp.hs https://github.com/filippovitale/eta-playground/tree/master/javafx-teastorm
  37. 51.

    start :: Stage -> Java TeaStormApp () start primaryStage =

    do t0 <- nanoTime c <- newCanvas w h s <- (newGroup c) >>= newScene loop <- newAnimationLoop c t0 n u m loop <.> startAnimation primaryStage <.> (setTitle "Eta-JavaFX TeaStorm" >> setScene s >> show) where n = 96 m = 10 w = n * m h = n * m u = (min w h) / n src/TeaStormApp.hs
  38. 52.

    name: javafx-teastorm version: 0.1.0.0 homepage: https://github.com/filippovitale/eta-playground/javafx-teastorm executable javafx-teastorm main-is: Main.hs

    java-sources: java/teastorm/AnimationLoop.java build-depends: base >=4.8 && <4.9 hs-source-dirs: src default-language: Haskell2010 javafx-teastorm.cabal
  39. 53.

    public class AnimationLoop extends AnimationTimer { public AnimationLoop(Canvas c, long

    t0, double n, double u, double m) {...} private static double computeIntensity(final double x, final double y, final double s, final double t) { double intensity = 1; for (double d = intensity * 4, df = 1; .025 < df * intensity; intensity -= .025) { double X = d * x / s - d / 2; double Y = d * y / s - d / 2; double Z = d / 2 - 9; df = (X * X + Y * Y * Math.cos(t/6 + Math.cos(d - X - Y)) + Z * Z) / 12 - 1 + Math.cos(X + t) * Math.cos(Y - t); d += df; } return intensity; } @Override public void handle(long currentNanoTime) { double t = (currentNanoTime - t0) / (500 * oneMillisecondInNanoTime); gc.clearRect(0, 0, w, h); // clear the Canvas for (int x = 0; x < n; x++) { for (int y = 0; y < n; y++) { double i = computeIntensity(x, y, n, t); gc.setGlobalAlpha(i); gc.fillOval(x * u, y * u, i * m * 1.4, i * m * 1.4); } } } }
  40. 54.

    $ etlas run Resolving dependencies... Configuring javafx-teastorm-0.1.0.0... Preprocessing executable 'javafx-teastorm'

    for javafx-teastorm-0.1.0.0.. Building executable 'javafx-teastorm' for javafx-teastorm-0.1.0.0.. [1 of 6] Compiling JavaFX.Types [2 of 6] Compiling JavaFX.Methods [3 of 6] Compiling TeaStormApp [4 of 6] Compiling JavaFX.Core [5 of 6] Compiling JavaFX [6 of 6] Compiling Main Linking dist/build/javafx-teastorm/javafx-teastorm.jar ... Running javafx-teastorm... https://github.com/filippovitale/eta-playground/tree/master/javafx-teastorm
  41. 59.

    name: scala-akka-stream-eta-wookiee version: 0.1.0.0 cabal-version: >=1.10 executable scala-akka-stream-eta-wookiee main-is: Main.hs

    build-depends: base >=4.8 && <4.9, bytestring, containers, aeson hs-source-dirs: src default-language: Haskell2010 -- java-sources: lib/manually-downloaded-akka-stream-uber.jar
  42. 60.

    name: scala-akka-stream-eta-wookiee version: 0.1.0.0 cabal-version: >=1.10 executable scala-akka-stream-eta-wookiee main-is: Main.hs

    build-depends: base >=4.8 && <4.9, bytestring, containers, aeson hs-source-dirs: src default-language: Haskell2010 maven-depends: com.typesafe.akka:akka-stream_2.12:2.4.17
  43. 61.

    name: scala-akka-stream-eta-wookiee version: 0.1.0.0 cabal-version: >=1.10 executable scala-akka-stream-eta-wookiee main-is: Main.hs

    build-depends: base >=4.8 && <4.9, bytestring, containers, aeson hs-source-dirs: src default-language: Haskell2010 maven-depends: com.typesafe.akka:akka-stream_2.12:2.4.17
  44. 62.

    src/AkkaStream/Exports.hs data {-# CLASS "stream.Example" #-} Example = Example (Object#

    Example) type JStringFlow = Flow JString JString NotUsed extractNameFlow :: Java Example JStringFlow extractNameFlow = undefined wookieeFlow :: Java Example JStringFlow wookieeFlow = undefined foreign export java extractNameFlow :: Java Example JStringFlow foreign export java wookieeFlow :: Java Example JStringFlow
  45. 63.

    src/AkkaStream/Exports.hs data {-# CLASS "stream.Example" #-} Example = Example (Object#

    Example) type JStringFlow = Flow JString JString NotUsed extractNameFlow :: Java Example JStringFlow extractNameFlow = flowFromFunction $ applyFunction f where f js = undefined wookieeFlow :: Java Example JStringFlow wookieeFlow = flowFromFunction $ applyFunction f where f js = undefined foreign export java extractNameFlow :: Java Example JStringFlow foreign export java wookieeFlow :: Java Example JStringFlow akka.stream.javadsl.Flow.fromFunction static <I,O> Flow<I,O,NotUsed> fromFunction(Function<I,O> f)
  46. 64.

    src/AkkaStream/Exports.hs data {-# CLASS "stream.Example" #-} Example = Example (Object#

    Example) type JStringFlow = Flow JString JString NotUsed extractNameFlow :: Java Example JStringFlow extractNameFlow = flowFromFunction $ applyFunction f where f js = undefined wookieeFlow :: Java Example JStringFlow wookieeFlow = flowFromFunction $ applyFunction f where f js = undefined foreign export java extractNameFlow :: Java Example JStringFlow foreign export java wookieeFlow :: Java Example JStringFlow akka.japi.function.Function trait Function[-T, +R] { def apply(param: T): R }
  47. 65.

    src/AkkaStream/Exports.hs data {-# CLASS "stream.Example" #-} Example = Example (Object#

    Example) type JStringFlow = Flow JString JString NotUsed extractNameFlow :: Java Example JStringFlow extractNameFlow = flowFromFunction $ applyFunction f where f js = return (toJava $ g $ pack (fromJava js :: [Char])) g s = (fromMaybe empty (decode s :: Maybe (Map String String))) ! "name" wookieeFlow :: Java Example JStringFlow wookieeFlow = flowFromFunction $ applyFunction f where f js = return (toJava $ (fromJava js :: [Char]) >>= wookiee) foreign export java extractNameFlow :: Java Example JStringFlow foreign export java wookieeFlow :: Java Example JStringFlow
  48. 66.

    src/main/scala/stream/Main.scala object Main extends App { val e = new

    Example() val extractNameFlow = e.extractNameFlow() val wookieeFlow = e.wookieeFlow() val jsonURL = getClass.getResource("/all_characters.json-stream.gz") val jsonPath = Paths.get(jsonURL.toURI) val lineSplitter = Framing.delimiter(ByteString("\n"), Int.MaxValue) FileIO.fromPath(jsonPath) .via(gunzip()) .via(lineSplitter) .map(_.utf8String) .via(extractNameFlow) .via(wookieeFlow) .runWith(Sink.foreach(println)) .onComplete(_ => system.terminate()) }
  49. 69.

    - Interactive REPL - Java FFI imports aid / helpers

    - Hot-code reloading as JVM has “built-in dynamism” - GraalVM on JDK9 - New JIT Compiler and - Polyglot Runtime for the JVM - Stability - Support for the fundamental Hackage libraries Eta active development areas