Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Eta

728 views

Published on

Eta / haskell experiments - presented on Lambda Days 2018

Published in: Software
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (2019 Update) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy & Proven Way to Build Good Habits & Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Get piano for all From This SECRET Link ▲▲▲ http://t.cn/AirWFeNM
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • ♣♣♣ http://t.cn/AirWFeNM
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/yxufevpm } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/yxufevpm } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/yxufevpm } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/yxufevpm } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/yxufevpm } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/yxufevpm } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • There is an error on slide 47... the function fibtco shoud be fibtco n = fibtcoinner (n-1) 1 0
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Eta

  1. 1. BEAUTY AND THEBEAUTY AND THE BEASTBEAST Eta Haskell for JVM
  2. 2. JAREK RATAJSKIJAREK RATAJSKI @jarek000000 Loves programming since the first line I wrote for C64 Anarchitect @ Engenius GmbH Java developer (since 1999) with a functional heart.
  3. 3. ORIGINORIGIN
  4. 4. Lots of developers that love Haskell
  5. 5. but when it comes to business...
  6. 6. SOLUTIONSSOLUTIONS
  7. 7. WRITEWRITE HASKELLHASKELL USING JAVAUSING JAVA return List.ofAll(iterableRead) .foldLeft(HashMap.<String, Topic>empty(), (existingMap, newElement) -> existingMap.put( newElemen existingMap.get( n .getOrElse .addMessag ); } catch (IOException e) { return HashMap.<String, Topic>empty(); }
  8. 8. WRITEWRITE HASKELLHASKELL USING SCALAUSING SCALA implicit val treeApplicative: Applicative[Tree] = new Applic def point[A](a: => A): Tree[A] = Tree(a, Seq.empty) def ap[A, B](fa: => Tree[A])(tf: => Tree[A => B]): Tree[B] val Tree(f, tfs) = tf Tree(f(fa.root), fa.children.map(t => t.map(f)) ++ tfs.m } }
  9. 9. WRITE HASKELL USING HASKELLWRITE HASKELL USING HASKELL (ETA)(ETA)
  10. 10. deploy to JVM
  11. 11. work with Java
  12. 12. TYPELEADTYPELEAD Company founded to create eta and in the future provide commercial support for it.
  13. 13. I am not associated with Typelead.
  14. 14. Whatever I say show here are mine own studies, with some help of typelead developers and community. I am neither experienced haskell nor eta developer. What I say might be wrong or may not reflect the reality or the future. I tried to do my best
  15. 15. We talk about half-finished product.
  16. 16. ETA 1.2.3 INTROETA 1.2.3 INTRO
  17. 17. QUICKSORT(*)QUICKSORT(*) quicksort [] = [] quicksort (x:xs) = quicksort left ++ [x] ++ quicksort right where left = [ y | y <- xs, y < x ] right = [ y | y <- xs, y >= x ] main = do let result = quicksort arr putStrLn $ show result where arr = [1,7,9,12,90,1,-1,22,0] $ eta Main.hs $ java -jar Main.jar [-1,0,1,1,7,9,12,22,90]
  18. 18. ETLAS (CABAL FOR ETA)ETLAS (CABAL FOR ETA) $ etlas init $ etlas build $ etlas run
  19. 19. For more info see pageEta tour
  20. 20. ETA SPECIALETA SPECIAL ETA =~=ETA =~= GHC FOR JVMGHC FOR JVM
  21. 21. backend for GHC -> great compatibility
  22. 22. STG machine Fibb.fibbtcoinner :: forall a_a7U9 a1_a7UA. (GHC.Classes.Eq a_a7U9, GHC.Num.Num a_a7U9, GHC.Num.Num a a_a7U9 -> a1_a7UA -> a1_a7UA -> a1_a7UA [GblId, Arity=6, Caf=NoCafRefs, Str=<S(C(C(S))L),U(C(C1(U)),A)><L,U(A,C(C1(U)),A,A,A,A,C(U))> Unf=OtherCon []] = r srt:SRT:[] [$dEq_s8MJ $dNum_s8MK $dNum1_s8ML eta_s8MM eta1_s8MN eta2_s8MO]
  23. 23. SPINELESS, TAGLESS G-MACHINESPINELESS, TAGLESS G-MACHINE
  24. 24. STGSTG ...It defines how the Haskell evaluation model should be efficiently implemented on standard hardware. ...
  25. 25. STG =~= (bytocode or llvm)
  26. 26. 1ST PHASE HS TO STG1ST PHASE HS TO STG Eta compiler in a phase .hs to STG ..is simply a GHC code! (forked)
  27. 27. 2ND PHASE2ND PHASE - STG TO BYTECODE /- STG TO BYTECODE / JVMJVM
  28. 28. 0: getstatic #127 // Field DZMZN:Leta/runti 3: ifnull 9 6: goto 35 9: ldc #3 // class ghc_prim/ghc/Typ 11: dup 12: astore_0 13: monitorenter 14: getstatic #127 // Field DZMZN:Leta/runt 17: ifnull 23 20: goto 33 23: new #129 // class ghc_prim/ghc/ty 26: dup 27: invokespecial #131 // Method ghc_prim/ghc/t 30: putstatic #127 // Field DZMZN:Leta/runt 33: aload_0
  29. 29. C IMPORTSC IMPORTS GHC supports native(C language) calls. (for instance used in Base packages)
  30. 30. Eta rewrites those parts to use jvm calls. original GHC Float.hs fragment Eta Float.hs fragment foreign import ccall unsafe "isFloatNaN" isFloatNaN :: Float - foreign import ccall unsafe "isFloatInfinite" isFloatInfinite foreign import ccall unsafe "isFloatDenormalized" isFloatDenor foreign import ccall unsafe "isFloatNegativeZero" isFloatNegat foreign import ccall unsafe "isFloatFinite" isFloatFinite :: F foreign import java unsafe "@static java.lang.Float.isNaN" isFloatNaN :: Float -> Bool foreign import java unsafe "@static java.lang.Float.isInfinite isFloatInfinite :: Float -> Bool foreign import java unsafe "@static eta.base.Utils.isFloatDeno isFloatDenormalized :: Float -> Bool foreign import java unsafe "@static eta.base.Utils.isFloatNega isFloatNegativeZero :: Float -> Bool foreign import java unsafe "@static eta.base.Utils.isFloatFini isFloatFinite :: Float -> Bool
  31. 31. ETLASETLAS Haskell GHC developers use cabal (or stack). Etlas is eta tool which is ~ cabal. It uses .cabal file format with extensions.
  32. 32. HACKAGEHACKAGE
  33. 33. Tons of libraries for haskell. De facto standard.
  34. 34. Categories: (3), - (1), .NET (9), Accessibility (3), ACME (49 AI (51), Algebra (35), Algorithm (3), Algorithm Visualization Anatomy (1), Animation (6), AOP (2), API (26), Apple (3), Appl Applicative (1), Argumentation (4), Arrows (5), Artificial In Aspect Oriented Programming (2), AST (1), Atom (1), ATS (8), Audio (13), Authentication (9), Automation (2), Avers (4), Av Benchmarking (11), Big Data (2), Binary (1), Bindings (39), B Bitcoin (12), Blockchain (1), Browser (7), BSD (1), Bsd3 (1) Builders (1), Business (3), ByteString (3), ByteStrings (1),
  35. 35. ETA HACKAGE PATCHESETA HACKAGE PATCHES
  36. 36. Project typelead/hackage == patches for common hackage projects.
  37. 37. Mostly 1 to 1 native C to Java calls changes. https://github.com/typelead/eta- hackage/blob/master/patches/text-1.2.2.2.patch {-# INLINE equal #-} -foreign import ccall unsafe "_hs_text_memcpy" memcpyI +foreign import java unsafe "@static eta.text.Utils.memcpy" m :: MutableByteArray# s -> CSize -> ByteArray# -> CSize - -foreign import ccall unsafe "_hs_text_memcmp" memcmp +foreign import java unsafe "@static eta.text.Utils.memcmp" m :: ByteArray# -> CSize -> ByteArray# -> CSize -> CSize -
  38. 38. SUPPORTS COMPILESUPPORTS COMPILE EXTENSIONSEXTENSIONS
  39. 39. {-# LANGUAGE FlexibleContexts, DataKinds, TypeFamilies, RankNT -- whatever
  40. 40. Eta is as close as you can get with Haskell/GHC on JVM
  41. 41. Lots of crazy haskell codes that use GHC extensions work on Eta without any problems.
  42. 42. BASICBASIC OPTIMISATIONSOPTIMISATIONS
  43. 43. TCOTCO
  44. 44. Naive fibonacci fibnaive 0 = 1 fibnaive 1 = 1 fibnaive n = fibnaive ( n-1) + fibnaive ( n - 2)
  45. 45. better fibtcoinner 0 sum presum = sum fibtcoinner n sum presum = fibtcoinner (n-1) (sum + presum) s fibtco n = fibbtcoinner n 1 0
  46. 46. Java private static BigInteger fibonacci(int n, BigInteger sum, Big if ( n== 0) { return sum; } else { return fibonacci(n-1, sum.add(presum), sum); } }
  47. 47. How much java stands?
  48. 48. Eta fibtcoinner 0 sum presum = sum fibtcoinner n sum presum = fibtcoinner (n-1) (sum + presum) s fibtco n = fibbtcoinner n 1 0
  49. 49. First results....
  50. 50. [1,1,2,4,8,16,32,64,128,256,512...]
  51. 51. BUG #603BUG #603 It took couple of nights to fix this bug. I've Learned ...haskell
  52. 52. Error while(var8) { Main.sat_s7YH var12 = new Main.sat_s7YH(var3); var1.R1 = var2; Closure var13 = Classes.zeze().enter(var1).apply2(var1, ( if (!(var13 instanceof False)) { return ((Closure)var10).evaluate(var1); } Main.sat_s7YM var14 = new Main.sat_s7YM(var4, (Closure)va Main.sat_s7YL var15 = new Main.sat_s7YL(var3, (Closure)va var9 = var15; //assign n-1 var10 = var14; //assign new sum var11 = var14; //assign presum var8 = true;
  53. 53. while( n > 0) { n = n -1; newSum = presum + sum sum = newSum presum = sum }
  54. 54. from http://geek-and-poke.com
  55. 55. while( n > 0) { n = n -1; newSum = presum + sum presum = sum // swapped sum = newSum // swapped
  56. 56. Fix
  57. 57. Fix compiler of Haskell written in Haskell (ghc) while learning haskell.
  58. 58. withContinuation unknownCall contCode lastCode JumpToIt label cgLocs mLne -> do JumpToIt label c traceCg (str "cgIdApp: JumpToIt") traceCg (st - codes <- getNonVoidArgCodes args + deps <- depe - emit $ multiAssign cgLocs codes + let sorted = + codes <- getNonVoidArgCodes $ arg <$> sorted + emit $ multiAssign (from <$> sorted) codes <> maybe mempty <> maybe mempty ((target, targetLoc) -> ( storeLoc targetLoc (iconst (locFt targetLoc mLne mLne <> goto label <> goto label +data LocalDep = LocalDep Int Int +{-
  59. 59. Decompiled eta. Fixed. while(var8) { Main.sat_s7YH var12 = new Main.sat_s7YH(var3); var1.R1 = var2; Closure var13 = Classes.zeze().enter(var1).apply2(var1, ( if (!(var13 instanceof False)) { return ((Closure)var10).evaluate(var1); } Main.sat_s7YM var14 = new Main.sat_s7YM(var4, (Closure)va Main.sat_s7YL var15 = new Main.sat_s7YL(var3, (Closure)va var11 = var10; //assign presum var10 = var14; //assign new sum var9 = var15; //assign n-1 var8 = true; }
  60. 60. HOW MUCH ETA STANDS???HOW MUCH ETA STANDS???
  61. 61. main = print $ show $ fibtco 100000
  62. 62. TRAMPOLINETRAMPOLINE import Control.Monad.Trans.Cont fibCps::Int->Cont r Int fibCps 0 = return 1 fibCps 1 = return 1 fibCps n = do n1 <- fibCps $ n-1 n2 <- fibCps $ n-2 return $ n1 + n2 main = do let result = trampoline $ runCont ( fibCps 100 ) id putStrLn $ show result
  63. 63. PERFORMANCEPERFORMANCE
  64. 64. JMH Quick sort implementations exported and called from java naive and real quicksort compared to same solutions in Java (using vavr.io) not very professional - just to get some overview
  65. 65. Naive quicksort Eta Naive quicksort Java/vavr quicksort [] = [] quicksort (x:xs) = quicksort left ++ [x] ++ quicksort right where left = [ y | y <- xs, y < x ] right = [ y | y <- xs, y >= x ] private List<Integer> qsort(List<Integer> input) { if (!input.isEmpty()) { final int middle = input.head(); final List<Integer> left = input.tail().filter( final List<Integer> right = input.tail().filter return qsort(left).appendAll(qsort(right).prepe } else { return input; } }
  66. 66. Real quicksort ETA qvsort :: (G.Vector v a, Ord a) => v a -> v a qvsort = G.modify go where go xs | M.length xs < 2 = return () | otherwise = do p <- M.read xs (M.length xs `div` 2) j <- M.unstablePartition (< p) xs let (l, pr) = M.splitAt j xs k <- M.unstablePartition (== p) pr go l; go $ M.drop k pr myvsort ::[Int] ->[Int] myvsort li = let vec = V.fromList li :: (V.Vector Int) sorted = qvsort vec :: (V.Vector Int) converted = V.toList sorted :: [Int]
  67. 67. Real quicksort Java (*) list.sort(); // :-)
  68. 68. Results
  69. 69. VS OTHERVS OTHER HASKELLSHASKELLS 12 Queens
  70. 70. {-# LANGUAGE BangPatterns #-} -- solution by Oystein Kolsrud -- https://www.youtube.com/watch?v=I2tMmsZC1ZU okToAdd :: Int -> [Int] -> Bool okToAdd q qs = all (okToAddDirection q qs) [succ, pred, id] where okToAddDirection q qs f = and $ zipWith (/=) (tail extendSolution n qs = map (q -> q:qs) $ filter (q -> okTo allSolutions !n 0 = [[]] allSolutions !n k = concatMap (extendSolution n) (allSoluti
  71. 71. Implementation Task Solutions Time (real) Frege 12 Queens 14200 Solutions (*)45.816s Eta 12 Queens 14200 Solutions (*)26.472s Ghc 12 Queens 14200 Solutions 9.806s
  72. 72. Unfair benchmark - both frege and eta were measured with JVM startup time.
  73. 73. JAVAJAVA INTEROPABILITYINTEROPABILITY
  74. 74. JWT - JAVA TYPESJWT - JAVA TYPES data JColor = JColor @java.awt.Color deriving Class
  75. 75. FOREIGN IMPORTFOREIGN IMPORT foreign import java unsafe "getGreen" getGreen :: Java JColor Int
  76. 76. Java is a Monad. -- Execute a Java action in the IO monad. java :: Java c a -> IO a -- Execute a Java action in the IO monad with respect to the -- given object. javaWith :: (Class c) => c -> Java c a -> IO a -- 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 withObject :: (Class c) => c -> Java c a -> Java b a -- Chain Java actions. (>-) :: (Class b) => Java a b -> Java b c -> Java a c
  77. 77. FOREIGN EXPORTFOREIGN EXPORT foreign export java "@static eta.example.MyExportedClass.sort" sort :: JIntArray -> JIntArray
  78. 78. STYLES OFSTYLES OF INTEROPERATIBILIYINTEROPERATIBILIY
  79. 79. FULL HASKELL WAYFULL HASKELL WAY Example: WAI Servlet appAll :: FilePath -> Application appAll filePath req respond = case pathInfo req of ["state"] -> appState (unsafePerformIO $ newMVar 0) r ["stream"] -> appStream req respond ["request-info"] -> appShowReq req respond ["static-file"] -> appFile filePath req respond _ -> appSimple req respond
  80. 80. CLASSES IN JAVA LOGIC INCLASSES IN JAVA LOGIC IN HASKELLHASKELL Types defined in java Haskell functions wok on Java objects Support and use of Java frameworks, serializations, db bindings, jsons.
  81. 81. Hint: in 2018a most of java frameworks do not need classical/ ugly JavaBeans anymore.
  82. 82. @JsonDeserialize public class Ball extends GameObject { private static final long serialVersionUID = 1L; public final Vector2D speed; @JsonCreator public Ball(float x, float y, Vector2D speed) { super(x, y); this.speed = speed; }
  83. 83. @JsonDeserialize public class GameState implements Serializable { private static final long serialVersionUID = 1L; public final GamePhase phase; public final Ball ball; public final Players players; public final long updateTime; @JsonCreator public GameState( final Ball ball, final Players players, final long updateTime) { this.ball = ball; this.players = players;
  84. 84. foreign import java unsafe "@new" newGameState :: Ball.Ball - foreign import java unsafe "@field phase" phase :: GameState - foreign import java unsafe "@field ball" ball :: GameState -> foreign import java unsafe "@field players" players :: GameSta foreign import java unsafe "@field updateTime" updateTime :: G push::GameState->Int64->J.Random->IO GameState push state time rnd | (aPhase == GamePhase.started ) = pushStarted state | otherwise = return state where aPhase = phase state
  85. 85. Linguistic determinism
  86. 86. from http://postcogtopics.blogspot.com/2016/
  87. 87. //A piece of smart code in Players should reduce both meth private Tuple2<Ball, Players> bouncePlayer1(final Players if (this.x < 0 && speed.x < 0) { if (isTouchingPaddle(players.player1.paddle, this. return Tuple.of(new Ball(0f, this.y, this.spee } else { return Tuple.of(Ball.randomDirection(rnd), pla } } return Tuple.of(this, players); } private Tuple2<Ball, Players> bouncePlayer2(final Players if (this.x > 1.0f && speed.x > 0) { if (isTouchingPaddle(players.player2.paddle, this.
  88. 88. bouncePlayerInternal::Ball->Players.Players->J.Random->(Lens' bouncePlayerInternal ball players rnd lens opLens xposition | (isTouchingPaddle paddle thisY) = return (newBall xpos | otherwise = do randomBall <- randomDirection rnd return ( randomBall, set opLens opponentScored playe where thisX = xObj ball thisY = yObj ball thisSpeed = speed ball speedX = Vector2D.x thisSpeed playerView = view lens players opponentScored = Player.incScore $ view opLens players paddle = Player.paddle playerView
  89. 89. HAVAHAVA ballBounceP :: Ball.Ball -> Players.Players -> J.Random -> IO Players.Players
  90. 90. POINTER REFPOINTER REF WAYWAY
  91. 91. Data in haskell, businell logic in haskell. Java as Controller.
  92. 92. We need to expose haskell objects to java.
  93. 93. Game of life data Color = Color {red :: Int, g b data Cell = Dead | Alive {color :: Color} type Row = Array Int Cell type Plane = Array Int Row type GOLState = StablePtr Plane initEmptyXP:: Int -> Int -> IO GOLState initEmptyXP wi hi = newStablePtr $ makePlane wi hi newStateXP::GOLState -> IO GOLState public static int newState(int var0) { return ((StablePtr)Runtime.evalIO(new Ap2Upd(TopHandle }
  94. 94. PROBLEMSPROBLEMS lot of imports to write for every simple java class this will be fixed thanks to ffi tool it took me a while to find out how to pass state between haskell and java other bug found (and resolved) java monad / io monad - not totally intuitive (for a newbie)
  95. 95. ETA VS FREGEETA VS FREGE
  96. 96. I used Frege very shortly. Frege is more mature Interoperation with Java is easier with Frege Frege will not be close GHC in the near future at the semantics level at the base libraries level
  97. 97. ETA FOR YOUETA FOR YOU
  98. 98. ETA NOWETA NOW
  99. 99. Eta is 0.7.0b2 is not production quality yet
  100. 100. If You think of eta in production soon -> talk to Typelead. They want to provide commercial support - ask them for conditions.
  101. 101. If you are haskell developer that wants to evaluate haskell on JVM Try it now!
  102. 102. If you are JVM / JavaDeveloper that wants to learn and play with Haskell Play now!
  103. 103. ETA COMMUNITYETA COMMUNITY
  104. 104. Small.
  105. 105. Great!
  106. 106. You can help! There are lot of small things to do.
  107. 107. Future of eta lies in your hands

×