Haskell + Scala ハイブリッド開発大作戦 #ScalaMatsuri / Operation Haskell + Scala

Haskell + Scala ハイブリッド開発大作戦 #ScalaMatsuri / Operation Haskell + Scala

ScalaMatsuri 2018 で使用したスライドです。Typelead 社によって開発中の JVM 用 Haskell である Eta 言語について、Java/Scala との相互呼び出しに焦点を当てて解説しています。また後半では Scala プロジェクトを部分的に Eta で置き換えるためのベストプラクティスについても触れています。

講演概要 : http://2018.scalamatsuri.org/ja/candidates/CheshireCat_1/
録画 : https://www.youtube.com/watch?v=u9WACa-JRA4

Slides for ScalaMatsuri 2018. Eta, Haskell on JVM, is now developed by Typelead Inc. This presentation provides you ideas of Eta's ingenious way of Java/Scala interoperation, and a sort of best practice to gradually introduce Eta-parts in your Scala projects.

Call for Proposal: http://2018.scalamatsuri.org/en/candidates/CheshireCat_1/

332f89cc697355902a817506b6995f2b?s=128

y_taka_23

March 16, 2018
Tweet

Transcript

  1. 1.

    Operation Haskell + Scala Towards Hybrid Functional Development Haskell +

    Scala ハイブリッド開発大作戦 Cheshire Cat (@y_taka_23) ScalaMatsuri 2018 (2018/03/17) #ScalaMatsuri
  2. 5.

    What’s Eta Lang? • Haskell on JVM ◦ OSS by

    Typelead Inc. in India ◦ Highly compatible with Haskell • Generates Java bytecode ◦ Smooth interoperation with JVM languages • Forked from GHC ◦ The de-facto standard Haskell compiler Eta は JVM 上で動く Haskell GHC からのフォークで、バイトコードを生成 #ScalaMatsuri
  3. 6.

    Contents • Purity in Haskell ◦ Haskell, what a procedural

    language ◦ What is the biggest difference from Scala? • Java/Scala interoperation ◦ Java Monads ◦ Type-level programming • Example of hybrid-project building 純粋性、Java/Scala との相互呼び出し ハイブリッドプロジェクトの例 #ScalaMatsuri
  4. 8.

    Maybe You Guys Think... • Haskell is too complicated ◦

    Functional programing is Greek to me... ◦ That’s a language for mathematicians… ◦ Category theory... • Haskell is poor at IO ◦ Even “Hello, world” requires understanding of monads ◦ Never be a production-ready language Haskell は数学的で難解という先入観 #ScalaMatsuri
  5. 9.

    Naive FizzBuzz in Scala for (i <- 1 to 100)

    { if (i % 3 == 0 && i % 5 == 0) { println("FizzBuzz") } else if (i % 3 == 0) { println("Fizz") } else if (i % 5 == 0) { println("Buzz") } else { println(i) } } Scala による単純な FizzBuzz #ScalaMatsuri
  6. 10.

    Also Naive FuzzBuzz in Haskell main :: IO () main

    = do forM_ [1..100] $ \i -> do if i `mod` 3 == 0 && i `mod` 5 == 0 then putStrLn "FizzBuzz" else if i `mod` 3 == 0 then putStrLn "Fizz" else if i `mod` 5 == 0 then putStrLn "Buzz" else putStrLn $ show i Haskell による単純な FizzBuzz #ScalaMatsuri
  7. 11.

    Also Naive FuzzBuzz in Haskell main :: IO () main

    = do forM_ [1..100] $ \i -> do if i `mod` 3 == 0 && i `mod` 5 == 0 then putStrLn "FizzBuzz" else if i `mod` 3 == 0 then putStrLn "Fizz" else if i `mod` 5 == 0 then putStrLn "Buzz" else putStrLn $ show i Haskell による単純な FizzBuzz for-loop #ScalaMatsuri
  8. 12.

    Also Naive FuzzBuzz in Haskell main :: IO () main

    = do forM_ [1..100] $ \i -> do if i `mod` 3 == 0 && i `mod` 5 == 0 then putStrLn "FizzBuzz" else if i `mod` 3 == 0 then putStrLn "Fizz" else if i `mod` 5 == 0 then putStrLn "Buzz" else putStrLn $ show i Haskell による単純な FizzBuzz if-conditional #ScalaMatsuri
  9. 13.

    Also Naive FuzzBuzz in Haskell main :: IO () main

    = do forM_ [1..100] $ \i -> do if i `mod` 3 == 0 && i `mod` 5 == 0 then putStrLn "FizzBuzz" else if i `mod` 3 == 0 then putStrLn "Fizz" else if i `mod` 5 == 0 then putStrLn "Buzz" else putStrLn $ show i Haskell による単純な FizzBuzz stdout #ScalaMatsuri
  10. 14.

    Haskell is Not Rocket Science • Procedural programming ◦ You

    don’t need to be too “functional” ◦ Remember Scala as “better Java” ◦ Naive sequantials / conditionals / loops • Mathematical background is not mandatory ◦ HelloWorld / FizzBuzz In the usual way • What’s difference of Haskell from Scala? Haskell は手続き的なコードも書ける では Scala との違いは何か? #ScalaMatsuri
  11. 15.

    Scala における純粋・非純粋なメソッド def pureMessage(name: String): String = { "Hello, "

    ++ name } def inpureMessage(name: String): String = { val another = io.StdIn.readLine() "Hello, " ++ name ++ ", " ++ another } #ScalaMatsuri
  12. 16.

    Scala における純粋・非純粋なメソッド def pureMessage(name: String): String = { "Hello, "

    ++ name } def inpureMessage(name: String): String = { val another = io.StdIn.readLine() "Hello, " ++ name ++ ", " ++ another } side-effect #ScalaMatsuri
  13. 17.

    Scala における純粋・非純粋なメソッド def pureMessage(name: String): String = { "Hello, "

    ++ name } def inpureMessage(name: String): String = { val another = io.StdIn.readLine() "Hello, " ++ name ++ ", " ++ another } #ScalaMatsuri
  14. 18.

    Haskell における純粋・非純粋な関数 pureMessage :: String -> String pureMessage name =

    "Hello, " ++ name inpureMessage :: String -> String inpureMessage name = do another <- getLine return $ "Hello, " ++ name ++ ", " ++ another #ScalaMatsuri
  15. 19.

    Haskell における純粋・非純粋な関数 pureMessage :: String -> String pureMessage name =

    "Hello, " ++ name inpureMessage :: String -> String inpureMessage name = do another <- getLine return $ "Hello, " ++ name ++ ", " ++ another type error #ScalaMatsuri
  16. 20.

    Haskell における純粋・非純粋な関数 pureMessage :: String -> String pureMessage name =

    "Hello, " ++ name inpureMessage :: String -> IO String inpureMessage name = do another <- getLine return $ "Hello, " ++ name ++ ", " ++ another compilable #ScalaMatsuri
  17. 21.

    Haskell における純粋・非純粋な関数 pureMessage :: String -> String pureMessage name =

    "Hello, " ++ name inpureMessage :: String -> IO String inpureMessage name = do another <- getLine return $ "Hello, " ++ name ++ ", " ++ another #ScalaMatsuri
  18. 22.

    IO as a Type in Haskell • Side effects should

    be separated ◦ Regardless of which programming languages ◦ Not predictable nor testable ◦ No built-in methodology for dealing IO in Scala • In Haskell, it appears as a type ◦ Compiler can check it at compilation time ◦ No careless side effects Haskell では IO が型に現れるため コンパイラがチェック可能 #ScalaMatsuri
  19. 23.

    Recap of Section. 1 • Haskell in the naive ways

    ◦ Procedural programming if you want ◦ Without (explicit) math objects, like monads • Side effects appear as the IO Type ◦ Compiler can check the “real world dependency” ◦ Quarantine “impure” parts from “pure” parts Haskell は手続き的な記述も可能だが 副作用が型として見える点で一線を画す #ScalaMatsuri
  20. 25.

    Advantages of JVM Languages • Platform independent ◦ 3 billion

    devices run JVM • Mature ecosystem ◦ Reuse your existing in-house/3rd party libraries • Gradual/partial introduction ◦ Small start, without change of infrastructure ◦ Huge market for Java developers 既存のエコシステムに乗ることで 移行コストを低くできるのは JVM 言語の利点 #ScalaMatsuri
  21. 28.

    Exporting Eta Functions module Util where import Java capitalize ::

    JString -> JString capitalize = ... foreign export java "@eta.example.Util.capitalize" capitalize :: JString -> JString Eta の関数のエクスポート #ScalaMatsuri
  22. 29.

    Exporting Eta Functions module Util where import Java capitalize ::

    JString -> JString capitalize = ... foreign export java "@eta.example.Util.capitalize" capitalize :: JString -> JString Eta の関数のエクスポート @FQDN.functionName #ScalaMatsuri
  23. 30.

    Importing as a Static Method import eta.example.Util object Main {

    def main(args: Array[String]): Unit = { val str = "hello, eta interop!" println(Util.capitalize(str)) } } Java/Scala 側からは静的メソッドに見える #ScalaMatsuri
  24. 31.

    Importing as a Static Method import eta.example.Util object Main {

    def main(args: Array[String]): Unit = { val str = "hello, eta interop!" println(Util.capitalize(str)) } } Java/Scala 側からは静的メソッドに見える singleton method #ScalaMatsuri
  25. 33.

    Two Big Hurdles in Java Interop • State handling ◦

    In OOP, objects have their internal states ◦ Java methods are potentially polluted by IO ◦ It looks conflicting with the purity in Haskell • Class inheritance ◦ Haskell doesn’t have subtyping ◦ Many Java stuffs depend on inheritance Java 連携で問題になるのは状態の扱いと継承 #ScalaMatsuri
  26. 36.

    Toy Model of of “States” • Manipulate a stack ◦

    The Simplest example of internal states • Simplify the model ◦ Contents are integers ◦ Pop an integer from the top of stack ◦ Push an integer x on the top of stack • Emulate the stack as pure functions 単純化した「状態」のモデル #ScalaMatsuri
  27. 37.

    Requirements for Stack Manipulation • Stack is immutable per se

    ◦ Need to return the “modified” stack as a result • Concatenate sequential actions ◦ “Doing act1, and then act2” should be an action • Action depends on actions in the past ◦ “Pop x and then pop y, and then push (x + y)” 欲しい Stack 操作の条件 イミュータブル、逐次処理、以前の結果の利用 #ScalaMatsuri
  28. 38.

    Immutable Stack type Stack = [Int] pop :: Stack ->

    (Int, Stack) pop (x : xs) = (x, xs) push :: Int -> Stack -> ((), Stack) push x = ((), x : xs) 変更されたスタックも一緒に返す #ScalaMatsuri
  29. 39.

    Immutable Stack type Stack = [Int] pop :: Stack ->

    (Int, Stack) pop (x : xs) = (x, xs) push :: Int -> Stack -> ((), Stack) push x = ((), x : xs) 変更されたスタックも一緒に返す type synonym #ScalaMatsuri
  30. 40.

    Immutable Stack type Stack = [Int] pop :: Stack ->

    (Int, Stack) pop (x : xs) = (x, xs) push :: Int -> Stack -> ((), Stack) push x = ((), x : xs) 変更されたスタックも一緒に返す (result, modified stack) #ScalaMatsuri
  31. 41.

    Immutable Stack type Stack = [Int] pop :: Stack ->

    (Int, Stack) pop (x : xs) = (x, xs) push :: Int -> Stack -> ((), Stack) push x = ((), x : xs) 変更されたスタックも一緒に返す result is Unit #ScalaMatsuri
  32. 42.

    Immutable Stack type Stack = [Int] pop :: Stack ->

    (Int, Stack) pop (x : xs) = (x, xs) push :: Int -> Stack -> ((), Stack) push x = ((), x : xs) 変更されたスタックも一緒に返す #ScalaMatsuri
  33. 43.

    Immutable Stack type Stack = [Int] type Action a =

    Stack -> (a, Stack) pop :: Action Int pop (x : xs) = (x, xs) push :: Int -> Action () push x = ((), x : xs) 変更されたスタックも一緒に返す #ScalaMatsuri
  34. 44.

    Sequential Actions andThen :: Action a -> Action b ->

    Action b andThen act1 act2 = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = act2 stack1 in (x2, stack2) myAct :: Action () myAct = push 1 `andThen` push 2 `andThen` push 3 操作を連結した結果も操作 #ScalaMatsuri
  35. 45.

    Sequential Actions andThen :: Action a -> Action b ->

    Action b andThen act1 act2 = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = act2 stack1 in (x2, stack2) myAct :: Action () myAct = push 1 `andThen` push 2 `andThen` push 3 操作を連結した結果も操作 concatenation #ScalaMatsuri
  36. 46.

    Sequential Actions andThen :: Action a -> Action b ->

    Action b andThen act1 act2 = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = act2 stack1 in (x2, stack2) myAct :: Action () myAct = push 1 `andThen` push 2 `andThen` push 3 操作を連結した結果も操作 pass the modified stack #ScalaMatsuri
  37. 47.

    Reuse results of the previous actions andThen :: Action a

    -> (a -> Action b) -> Action b andThen act1 factory = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = (factory x1) stack1 in (x2, stack2) myAct :: Action () myAct = pop `andThen` (\x -> pop `andThen` (\y -> push (x + y))) 以前の結果に依存した操作 #ScalaMatsuri
  38. 48.

    Reuse results of the previous actions andThen :: Action a

    -> (a -> Action b) -> Action b andThen act1 factory = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = (factory x1) stack1 in (x2, stack2) myAct :: Action () myAct = pop `andThen` (\x -> pop `andThen` (\y -> push (x + y))) 以前の結果に依存した操作 #ScalaMatsuri
  39. 49.

    Reuse results of the previous actions andThen :: Action a

    -> (a -> Action b) -> Action b andThen act1 factory = \stack -> let (x1, stack1) = act1 stack (x2, stack2) = (factory x1) stack1 in (x2, stack2) myAct :: Action () myAct = pop `andThen` (\x -> pop `andThen` (\y -> push (x + y))) 以前の結果に依存した操作 depends on x1 #ScalaMatsuri
  40. 50.

    andThen :: Action a -> (a -> Action b) ->

    Action b 最終的に得られた関数 #ScalaMatsuri
  41. 54.

    andThen :: Action a -> (a -> Action b) ->

    Action b 最終的に得られた関数 #ScalaMatsuri
  42. 55.

    (>>=) :: m a -> (a -> m b) ->

    m b Monad 型クラスの bind 関数 #ScalaMatsuri
  43. 56.

    Scala’s for-Comprehension // actual opt1.flatMap(x => opt2.map(y => x +

    y)) // sugared for { x <- opt1 y <- opt2 } yield { x + y } Scala の for 式 #ScalaMatsuri
  44. 57.

    Haskell’s do-Notation -- actual myAct = pop >>= (\x ->

    pop >>= (\y -> push (x + y))) -- sugared myAct :: Action () myAct = do x <- pop y <- pop push (x + y) Haskell の do 記法 #ScalaMatsuri
  45. 58.

    Haskell’s do-Notation -- actual myAct = pop >>= (\x ->

    pop >>= (\y -> push (x + y))) -- sugared myAct :: Action () myAct = do x <- pop y <- pop push (x + y) Haskell の do 記法 #ScalaMatsuri
  46. 59.

    Haskell’s do-Notation -- actual myAct = pop >>= (\x ->

    pop >>= (\y -> push (x + y))) -- sugared myAct :: Action () myAct = do x <- pop y <- pop push (x + y) Haskell の do 記法 hidden Stack #ScalaMatsuri
  47. 60.

    What’s the Java Monads? • DSL for the interop ◦

    Use monads as a framework • Code block of a type “Java a b” ◦ a: Type of the “implicit” method receiver ◦ b: Type of the final execution result ◦ “Java a” is a monad, depending on a • IO is a special case of Java monads Java モナドは Java 呼び出しのための DSL レシーバと戻り値の組で型が決まる #ScalaMatsuri
  48. 61.

    -- add :: e -> Java (Deque e) Boolean --

    poll :: Java (Deque e) e myAct1 :: Java (Deque Int) Boolean myAct1 = add 1 myAct2 :: Java (Deque Int) Bool myAct2 = do add 2 add 1 x <- poll y <- poll add (x + y) Deque クラスによる Java モナドの例 #ScalaMatsuri
  49. 62.

    -- add :: e -> Java (Deque e) Boolean --

    poll :: Java (Deque e) e myAct1 :: Java (Deque Int) Boolean myAct1 = add 1 myAct2 :: Java (Deque Int) Bool myAct2 = do add 2 add 1 x <- poll y <- poll add (x + y) Deque クラスによる Java モナドの例 hidden receiver :: Deque Int #ScalaMatsuri
  50. 63.

    Instance Methods data File = File @java.io.File deriving Class foreign

    import java unsafe "canExecute" canExecute :: Java File Bool インスタンスメソッドのインポート #ScalaMatsuri
  51. 64.

    Instance Methods data File = File @java.io.File deriving Class foreign

    import java unsafe "canExecute" canExecute :: Java File Bool インスタンスメソッドのインポート typeclass as a marker #ScalaMatsuri
  52. 65.

    Instance Methods data File = File @java.io.File deriving Class foreign

    import java unsafe "canExecute" canExecute :: Java File Bool インスタンスメソッドのインポート receiver :: File return value :: Bool #ScalaMatsuri
  53. 66.

    Constructors data File = File @java.io.File deriving Class foreign import

    java unsafe "@new" newFile :: String -> Java a File コンストラクタのインポート #ScalaMatsuri
  54. 67.

    Constructors data File = File @java.io.File deriving Class foreign import

    java unsafe "@new" newFile :: String -> Java a File コンストラクタのインポート no receiver return value :: File #ScalaMatsuri
  55. 68.

    Static Methods data File = File @java.io.File deriving Class foreign

    import java unsafe "@static java.io.File.createTempFile" createTempFile :: String -> String -> Java a File 静的メソッドのインポート #ScalaMatsuri
  56. 69.

    Static Methods data File = File @java.io.File deriving Class foreign

    import java unsafe "@static java.io.File.createTempFile" createTempFile :: String -> String -> Java a File 静的メソッドのインポート no receiver return value :: File #ScalaMatsuri
  57. 70.

    Glue of Java Monads java :: Java c a ->

    IO a javaWith :: (Class c) => c -> Java c a -> IO a <.> :: (Class c) => c -> Java c a -> Java b a >- :: (Class b) => Java a b -> Java b c -> Java a c io :: IO c -> Java c a メソッドレシーバーを入れ替える関数群 #ScalaMatsuri
  58. 72.

    foreign import java unsafe "@new" newFile :: String -> Java

    a File foreign import java unsafe toString :: Object -> String data File = File @java.io.File deriving Class main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係によるコンパイルエラー #ScalaMatsuri
  59. 73.

    foreign import java unsafe "@new" newFile :: String -> Java

    a File foreign import java unsafe toString :: Object -> String data File = File @java.io.File deriving Class main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係によるコンパイルエラー #ScalaMatsuri file :: File
  60. 74.

    foreign import java unsafe "@new" newFile :: String -> Java

    a File foreign import java unsafe toString :: Object -> String data File = File @java.io.File deriving Class main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係によるコンパイルエラー #ScalaMatsuri type error
  61. 75.

    {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE

    TypeFamilies #-} foreign import java unsafe toString :: (a <: Object) => a -> String type instance Inherits File = '[Object] main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係を踏まえた関数呼び出し #ScalaMatsuri
  62. 76.

    {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE

    TypeFamilies #-} foreign import java unsafe toString :: (a <: Object) => a -> String type instance Inherits File = '[Object] main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係を踏まえた関数呼び出し #ScalaMatsuri
  63. 77.

    GHC Extensions • GHC specialities ◦ Experimental futures, out of

    the language spec ◦ Eta, which is a fork of GHC, can use them • Enabled by special comments ◦ {-# LANGUAGE ExtensionNames #-} • Unavoidable for modern Haskell ◦ Especially abstract frameworks Eta は GHC のフォークなので コンパイラ拡張が使用可能 #ScalaMatsuri
  64. 78.

    Extensions for Inheritance • DataKinds ◦ Promote normal types to

    kinds • TypeFamilies ◦ Define type-level functions • TypeOperators ◦ Declare an infix operator for a 2-param typeclass 継承関係を表現するための三つの拡張 #ScalaMatsuri
  65. 79.

    Types and Kinds • Kinds are “types of types” ◦

    1, 2, 3, … :: Int ◦ Int, String, File, ... :: * ◦ Maybe :: * -> * • DataKinds promote datatypes ◦ Types to kinds, data constructors to types ◦ Enable to use values in type-level functions カインドは「型の型」 DataKinds 拡張でデータ型を昇格できる #ScalaMatsuri
  66. 80.

    TypeFamilies type family Inherits (a :: *) :: [*] data

    Defined = Yes | No type family Extends' (a :: Class) (b :: Class) :: Defined where Extends' a a = Yes Extends' a Object = Yes Extends' Object a = No Extends' a b = ExtendsList (Inherits a) b TypeFamilies 拡張を用いた型レベル計算 #ScalaMatsuri
  67. 81.

    TypeFamilies type family Inherits (a :: *) :: [*] data

    Defined = Yes | No type family Extends' (a :: Class) (b :: Class) :: Defined where Extends' a a = Yes Extends' a Object = Yes Extends' Object a = No Extends' a b = ExtendsList (Inherits a) b TypeFamilies 拡張を用いた型レベル計算 type-level function #ScalaMatsuri
  68. 82.

    TypeFamilies type family Inherits (a :: *) :: [*] data

    Defined = Yes | No type family Extends' (a :: Class) (b :: Class) :: Defined where Extends' a a = Yes Extends' a Object = Yes Extends' Object a = No Extends' a b = ExtendsList (Inherits a) b TypeFamilies 拡張を用いた型レベル計算 promoted kind #ScalaMatsuri
  69. 83.

    TypeFamilies type family Inherits (a :: *) :: [*] data

    Defined = Yes | No type family Extends' (a :: Class) (b :: Class) :: Defined where Extends' a a = Yes Extends' a Object = Yes Extends' Object a = No Extends' a b = ExtendsList (Inherits a) b TypeFamilies 拡張を用いた型レベル計算 a extends b? #ScalaMatsuri
  70. 84.

    TypeFamilies type family ExtendsList (a :: [*]) (b :: *)

    :: Defined where ExtendsList '[] y = No ExtendsList (x ': xs) y = Or (Extends' x y) (ExtendsList xs y) type family Or (a :: Defined) (b :: Defined) :: Defined where Or No No = No Or a b = Yes TypeFamilies 拡張を用いた型レベル計算 #ScalaMatsuri
  71. 85.

    TypeFamilies type family ExtendsList (a :: [*]) (b :: *)

    :: Defined where ExtendsList '[] y = No ExtendsList (x ': xs) y = Or (Extends' x y) (ExtendsList xs y) type family Or (a :: Defined) (b :: Defined) :: Defined where Or No No = No Or a b = Yes TypeFamilies 拡張を用いた型レベル計算 (one of a) extends b? #ScalaMatsuri
  72. 86.

    TypeOperators class (Class a, Class b) => Extends a b

    where superCast :: a -> b unsafeCast :: b -> a instance (Class a, Class b, Extends' a b ~ Yes) => Extends a b where type (<:) a b = Extends a b TypeOperators 拡張による型演算子の定義 #ScalaMatsuri
  73. 87.

    TypeOperators class (Class a, Class b) => Extends a b

    where superCast :: a -> b unsafeCast :: b -> a instance (Class a, Class b, Extends' a b ~ Yes) => Extends a b where type (<:) a b = Extends a b TypeOperators 拡張による型演算子の定義 #ScalaMatsuri
  74. 88.

    {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE

    TypeFamilies #-} foreign import java unsafe toString :: (a <: Object) => a -> String type instance Inherits File = '[Object] main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係を踏まえた関数呼び出し #ScalaMatsuri
  75. 89.

    {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE

    TypeFamilies #-} foreign import java unsafe toString :: (a <: Object) => a -> String type instance Inherits File = '[Object] main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係を踏まえた関数呼び出し TypeOperators #ScalaMatsuri
  76. 90.

    {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE

    TypeFamilies #-} foreign import java unsafe toString :: (a <: Object) => a -> String type instance Inherits File = '[Object] main :: IO () main = do file <- java $ newFile "text.txt" putStrLn $ toString file 継承関係を踏まえた関数呼び出し TypeFamilies #ScalaMatsuri
  77. 91.

    Recap of Section. 2 • Exposting Eta functions ◦ Straightforward,

    just as static methods • Java monads ◦ Method receiver can be considered as a monad • inheritance ◦ Type-level definitions by the GHC extensions Eta からエクスポートはそのまま インポートは Java モナドと型レベル記述を活用 #ScalaMatsuri
  78. 94.

    object Client { implicit val system = ActorSystem() implicit val

    materializer = ActorMaterializer() implicit val execContext = system.dispatcher val responseFuture: Future[HttpResponse] = Http().singleRequest( HttpRequest(uri = "https://akka.io")) responseFuture.onComplete { case Success(res) => println(res) case Failure(_) => sys.error("ERROR!") } } Scala による Akka HTTP Client のサンプル #ScalaMatsuri
  79. 95.

    apply メソッドの糖衣構文 // sugared val system = ActorSystem() // actual

    val system = ActorSystem.apply() data ActorSystem = ... foreign import java unsafe "@static akka.actor.ActorSystem.apply" newActorSystem :: Java a ActorSystem #ScalaMatsuri
  80. 96.

    apply メソッドの糖衣構文 // sugared val system = ActorSystem() // actual

    val system = ActorSystem.apply() data ActorSystem = ... foreign import java unsafe "@static akka.actor.ActorSystem.apply" newActorSystem :: Java a ActorSystem static method #ScalaMatsuri
  81. 97.

    // sugared val materializer = ActorMaterializer() /* actual * *

    ActorMaterializer.apply( * settings: Option[MaterializerSettings] = None, * prefix: Option[String] = None * ) (implicit context: ActorRefFactory) */ val materializer = ActorMaterializer(None, None, system) 実は大量のデフォルト・暗黙引数 #ScalaMatsuri
  82. 98.

    // sugared val materializer = ActorMaterializer() /* actual * *

    ActorMaterializer.apply( * settings: Option[MaterializerSettings] = None, * prefix: Option[String] = None * ) (implicit context: ActorRefFactory) */ val materializer = ActorMaterializer(None, None, system) 実は大量のデフォルト・暗黙引数 default / implicit args #ScalaMatsuri
  83. 99.

    data Option a = Option (@scala.Option a) data ActorRefFactory =

    ... data ActorMaterializerSettings = ... type instance Inherits ActorSystem = '[Object, ActorRefFactory] foreign import java unsafe "@static akka.stream.ActorMaterializer.apply" newActorMaterializer :: (a <: ActorRefFactory) => Option ActorMaterializerSettings -> Option JString -> a -> Java c ActorMaterializer 明示的なインポートが必要 #ScalaMatsuri
  84. 101.

    Difficulty in Scala Interop • Eta interop is “sugar free”

    ◦ Designed for Java interoperation ◦ Poor support for commonly-used Scala data types • Lots of boilerplates required ◦ Backfire of Scala’s sophisticated features ◦ Code generator is not well-developed • Better to use Java libraries Eta から Scala のライブラリを呼ぶと ボイラプレートが増えがち #ScalaMatsuri
  85. 102.

    Best Practice for Hybrid Projects • Phase 1: Eta inside

    Scala ◦ Eta is only for pure logic, like data transformation ◦ Scala parts invokes Eta ◦ Communicate with outside by Scala • Phase 2: Java Inside Eta ◦ Eta is for main parts of the service ◦ Eta invokes Java, to communicate others 第一段階:Scala 内に Eta を埋め込む 第二段階:Eta 内に Java を埋め込む #ScalaMatsuri
  86. 110.

    Etlas • Standard Build Tool for Eta ◦ Forked from

    Cabal in Haskell ◦ Similar to sbt in Scala • Dependency manager + task runner ◦ Common build definition with Haskell (.cabal) ◦ Accesses official Haskell package repos (Hackage) ◦ Fetches patches from eta-hackage Eta 用ビルドツール Etlas Haskell と共通の仕組みを利用 #ScalaMatsuri
  87. 111.

    Toy Example • Microservice written in Scala ◦ On Scala

    pipeline, built by sbt • Transform JSON ◦ Consider records like { “name”: “alice”, “age”: 17 } ◦ Capitalize “name” fields ◦ Add 1 to the “age” fields 簡単なサンプルプロジェクト #ScalaMatsuri
  88. 113.

    Directory Structure +-- project | +-- plugins.sbt +-- src +--

    main +-- scala | +-- example | +-- Main.scala +-- eta +-- example.cabal +-- Example +-- Transform.hs プロジェクトの構成 #ScalaMatsuri
  89. 114.

    Directory Structure +-- project | +-- plugins.sbt +-- src +--

    main +-- scala | +-- example | +-- Main.scala +-- eta +-- example.cabal +-- Example +-- Transform.hs プロジェクトの構成 embedded project #ScalaMatsuri
  90. 115.

    Cabal File for Etlas library build-depends: base , lens exported-moduels:

    Example.Transform default-language: Haskell2010 Etlas 用ビルド定義 #ScalaMatsuri
  91. 116.

    Cabal File for Etlas library build-depends: base , lens exported-moduels:

    Example.Transform default-language: Haskell2010 Etlas 用ビルド定義 Haskell library #ScalaMatsuri
  92. 117.

    module Example.Transform where fixJson :: JString -> JString fixJson =

    toJava . transform . fromJava where transform :: Text -> Text transform json = json & (key "name" . _String) %~ toUpper & (key "age" . _Integral) %~ (+1) foreign export java "@static eta.example.Transform.fixJson" fixJson :: JString -> JString Eta 側:純粋なロジック #ScalaMatsuri
  93. 118.

    module Example.Transform where fixJson :: JString -> JString fixJson =

    toJava . transform . fromJava where transform :: Text -> Text transform json = json & (key "name" . _String) %~ toUpper & (key "age" . _Integral) %~ (+1) foreign export java "@static eta.example.Transform.fixJson" fixJson :: JString -> JString Eta 側:純粋なロジック #ScalaMatsuri
  94. 119.

    package example import eta.example.Transform object Main extends App { val

    writer = new PrintWriter(new File("out.json")) Source.fromResource("in.json") .getLine.foreach { json => writer.write(Transform.fixJson(json) ++ "\n") } writer.close() } Scala 側:外界との inpure なやりとり #ScalaMatsuri
  95. 120.

    package example import eta.example.Transform object Main extends App { val

    writer = new PrintWriter(new File("out.json")) Source.fromResource("in.json") .getLine.foreach { json => writer.write(Transform.fixJson(json) ++ "\n") } writer.close() } Scala 側:外界との inpure なやりとり singleton method #ScalaMatsuri
  96. 121.

    sbt Plugin for Etlas +-- project | +-- plugins.sbt +--

    src +-- main +-- scala | +-- example | +-- Main.scala +-- eta +-- example.cabal +-- Example +-- Transform.hs プラグインの導入 #ScalaMatsuri
  97. 122.

    sbt Plugin for Etlas +-- project | +-- plugins.sbt +--

    src +-- main +-- scala | +-- example | +-- Main.scala +-- eta +-- example.cabal +-- Example +-- Transform.hs プラグインの導入 addSbtPlugin("com.typelead" % "sbt-eta" % "0.1.0") #ScalaMatsuri
  98. 124.

    Directory Structure +-- example.cabal +-- src | +-- Main.hs +--

    java +-- example +-- Util.java プロジェクトの構成 #ScalaMatsuri
  99. 125.

    Directory Structure +-- example.cabal +-- src | +-- Main.hs +--

    java +-- example +-- Util.java プロジェクトの構成 embedded project #ScalaMatsuri
  100. 126.

    Cabal File for Etlas executable eta-example main-is: Main.hs hs-source-dirs: src

    build-depends: base default-language: Haskell2010 java-sources: java/example/Util.java , /path/to/Util.class , /path/to/Util.jar maven-depends: com.example:awesome:x.y.z Etlas 用ビルド定義 #ScalaMatsuri
  101. 127.

    Cabal File for Etlas executable eta-example main-is: Main.hs hs-source-dirs: src

    build-depends: base default-language: Haskell2010 java-sources: java/example/Util.java , /path/to/Util.class , /path/to/Util.jar maven-depends: com.example:awesome:x.y.z Etlas 用ビルド定義 Java stuffs #ScalaMatsuri
  102. 128.

    Cabal File for Etlas executable eta-example main-is: Main.hs hs-source-dirs: src

    build-depends: base default-language: Haskell2010 java-sources: java/example/Util.java , /path/to/Util.class , /path/to/Util.jar maven-depends: com.example:awesome:x.y.z Etlas 用ビルド定義 Maven repository #ScalaMatsuri
  103. 129.

    Recap of Section. 3 • Scala libraries are hard to

    use from Eta ◦ Eta cannot handle Scala’s syntactic sugar ◦ In the most cases, Java version is better to import • Follow the best practice ◦ Firstly embed Eta into Scala ◦ Then embed Java into Eta • sbt, Maven repos + Etlas integration Eta から呼ぶなら Scala ではなく Java ビルドツールを通じて段階的に導入可能 #ScalaMatsuri
  104. 131.

    Tour of Eta • Online tutorials with a sandbox ◦

    https://tour.eta-lang.org • The best starting point for Eta newbies • Focusing on practical use ◦ Basics, like syntax and flow control ◦ “Haskell-ish” design patterns ◦ A little more practical than “Learn you a Haskell” Eta 公式のオンラインチュートリアル #ScalaMatsuri
  105. 133.

    Japan Haskell User Group (Haskell-jp) • Since Mar. 2017 •

    Monthly meetup ◦ a.k.a. Mokumoku-kai ◦ Newbies are welcome! ◦ https://haskell-jp.connpass.com • Slack / Reddit / Blog ◦ https://haskell.jp 日本 Haskell ユーザグループの活動 #ScalaMatsuri
  106. 135.

    Google Summer of Code • Ideas List (abr) ◦ Better

    type error messages ◦ Eta on Android ◦ High-performance Web server with Fibers threading ◦ Purity & nullability analysis • Deadline: 27th Mar. 16:00 UTC ◦ https://eta-lang.org/contribute/gsoc/2018 Google Summer of Code の対象プロジェクトに #ScalaMatsuri
  107. 137.

    Summary • IO in Haskell ◦ Compiler can check side-effect

    as a type • Java/Scala interoperation ◦ Java monads + type-level GHC extensions • Strategy for hybrid projects ◦ Haskell is for internal pure logic, inside Scala 本日のまとめ #ScalaMatsuri