Beyond Bash Shell scripting in a typed, OO language Scala by the - - PowerPoint PPT Presentation

beyond bash
SMART_READER_LITE
LIVE PREVIEW

Beyond Bash Shell scripting in a typed, OO language Scala by the - - PowerPoint PPT Presentation

Beyond Bash Shell scripting in a typed, OO language Scala by the Bay, 15 August 2015 Slides: http://tinyurl.com/beyondbash 0.1 Who am i Li Haoyi Paid $ to work on dev tools @ Dropbox Not paid $ to work on Scala.js Using Scala professionally


slide-1
SLIDE 1

Beyond Bash

Shell scripting in a typed, OO language Scala by the Bay, 15 August 2015 Slides: http://tinyurl.com/beyondbash

slide-2
SLIDE 2

0.1 Who am i

Li Haoyi Paid $ to work on dev tools @ Dropbox Not paid $ to work on Scala.js Using Scala professionally since… never

slide-3
SLIDE 3

0.2 Agenda

  • 0.x: Agenda
  • 1.x: Bash
  • 2.x: Ammonite-Ops
  • 3.x: Ammonite-REPL
  • 4.x: Conclusion
  • 5.x: Q&A
slide-4
SLIDE 4

0.3 Problem Statement

“How can we stop using the worst languages in the world to build our most important infrastructure?

slide-5
SLIDE 5

1.1 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ? DANGER DANGER DANGER DANGER

slide-6
SLIDE 6

1.2 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ? Safety Safety

slide-7
SLIDE 7

1.3 Scala.js!

Javascript: Problem solved Scala.js works Check it out if you haven't http://www.scala-js.org/

slide-8
SLIDE 8

1.3 Scala.js!

  • Casting is great elem.asInstanceOf[html.Input]

○ In Javascript, every expression is a cast!

  • Weird, unsound behavior is fine

○ As long as it’s less weird/unsound than Javascript

  • Best-effort error-handling is outstanding

○ Javascript doesn’t put in effort at all

slide-9
SLIDE 9

Bad when better than worse is excellent

slide-10
SLIDE 10

1.4 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ? Safety

Bash, Python, Puppet, Ruby, Vagrant...

Safety DANGER

slide-11
SLIDE 11

1.5 Danger Below!

High-performance, type-safe application code High-performance, type-safe web front-end Underpinned by a mix of Bash, Python, Ruby, Puppet, Vagrant, ...

slide-12
SLIDE 12

1.5 Danger Below!

Hard to test! Not typechecked! Worst consequences for errors

slide-13
SLIDE 13

1.5 Danger Below!

Server

Client Client

Database Server

Ok Ok

Bash, Python, Puppet, Ruby, Vagrant...

Ok Ok Down

slide-14
SLIDE 14

1.5 Danger Below!

Server

Client Client

Database Server

Ok Ok Ok Ok Down Down Down

Bash, Python, Puppet, Ruby, Vagrant...

Ok

slide-15
SLIDE 15

1.5 Danger Below!

Server

Client Client

Database Server

Ok Ok Ok Ok Down Down Down Down

Bash, Python, Puppet, Ruby, Vagrant...

slide-16
SLIDE 16

bash$

slide-17
SLIDE 17

1.6 What's wrong with Bash?

  • Obscure syntax if [[$? -eq 0 ]] if [[ $? -eq 0 ]]

○ Even though you use it every day for 10 yrs

  • Everything is global

○ Everything is spooky!

  • Everything is a String
  • Even basic math/logic is incredibly difficult
slide-18
SLIDE 18

1.7 What's wrong with Bash?

# Run a script on all files with some extension find . -name '*.ext' | while IFS=$'\n' read -r FILE; do process "$(readlink -f "$FILE")" || echo "error processing: $FILE" done find . -name '*.ext' \( -exec ./some_other_script "$PWD"/{} \; -o -print \) find . -name '*.ext' -exec ./some_other_script "$PWD"/{} \;

http://stackoverflow.com/questions/4410412/bash-processing-recursively-through-all-files-in-a-directory

It seems to work ??? I n c

  • r

r e c t ??? Good Solution! ???

slide-19
SLIDE 19

“It seems to work”

Such a high degree of confidence!

slide-20
SLIDE 20

Why do people use Bash

Can we use something else?

slide-21
SLIDE 21

Sample use case

  • List the things in my current folder
  • Look at my current git
  • Make a folder with a file inside
  • Delete the folder
slide-22
SLIDE 22

Why do people use Bash

Can we use something else?

slide-23
SLIDE 23

No

slide-24
SLIDE 24

Bash is Better

slide-25
SLIDE 25

1.9 Bash vs Scala

rm -rf folder/inner_dir

def removeAll(path: String) = { def rec(f: File): Seq[File] = f.listFiles .filter(_.isDirectory) .flatMap(rec) .++(f.listFiles) for(f <- rec(new File(path))){ if (!f.delete()) throw new RuntimeException() } } removeAll("folder/inner_dir")

1 line 24 chars 12 lines 279 chars

slide-26
SLIDE 26

1.10 Bash vs Python

rm -rf folder/inner_dir

import shutil shutil.rmtree('folder/my_file.jpg')

1 line 24 chars 2 lines 50 chars

slide-27
SLIDE 27

import subprocess subprocess.check_call(["git", "status"])

git status

1.11 Bash vs Python: Round 2

1 line 10 chars 2 lines 60 chars

Important Bits Dumb Noise

slide-28
SLIDE 28

1.12 Bash is Better

Less syntactic ceremony cp fileB fileB Common operations are short ls Fewer keystrokes overall Commands do what you want rm -rf folder Very Important!

slide-29
SLIDE 29

Ammonite-Ops

Rock-solid filesystem ops in Scala

"com.lihaoyi" %% "ammonite-ops" % "0.4.5"

slide-30
SLIDE 30

2.1 Ammonite-Ops

  • Goals:

○ No more than 2x as verbose as Bash ○ Safer than working with Python or java.{io, nio}

  • Non-Goals!

○ Monadic pure dependent-typed safety ○ Reactive manifesto accreditation ○ 50-year enterprise maintainability

slide-31
SLIDE 31

2.2 Ammonite-Ops

git status rm folder/my_file.jpg

%git 'status rm! 'folder/"my_file.jpg"

1 line 10 chars 1 line 21 chars 1 line 12 chars 1 line 25 chars

slide-32
SLIDE 32

2.3 A Taste of Ammonite

import ammonite.ops._ // Delete a file or folder rm! cwd/'folder // Make a folder named "folder" mkdir! cwd/'folder // Copy a file or folder cp(cwd/'folder, cwd/'folder1) // List the current directory val listed = ls! cwd

Short commands that mirror Bash That do what you want! No ambiguity in parsing arguments

slide-33
SLIDE 33

2.4 A Taste of Ammonite

// List the current directory val listed: Seq[Path] = ls! cwd // Commands return normal values // you can process normally for(path <- listed){ println(path) // paths are proper data-structures // with attributes, methods, etc. if (path.ext == "tmp") rm! path }

Values are typed, structured data No string munging trying to do simple tasks!

slide-34
SLIDE 34

2.5 Piping

things | f -> things map f things || f -> things flatMap f things |? f -> things filter f things |& f -> things reduce f things |! f -> things foreach f things |> f -> f(things) f! thing -> f(thing)

Traversable Any T => V

slide-35
SLIDE 35

2.6 Putting it Together

  • Concise filesystem operations

○ ls! cwd

  • Structured, concise path operations

○ ls! cwd/'src/'main

  • Pipes as aliases for collection methods

○ ls! cwd/'src/'main |? (_.ext == "scala") | (_.size) sum

slide-36
SLIDE 36

2.7 Putting it Together

# Recursive line count of Javascript files find ./dir -name '*.js' | xargs wc -l ls.rec! cwd/'dir |? (_.ext == "js") | read.lines | (_.size) sum

38 chars 64 chars

slide-37
SLIDE 37

2.8 Putting it Together

# List dot-files *only* ls -a | grep "^\." ls! cwd |? (_.last(0) == '.')

19 chars 30 chars

slide-38
SLIDE 38

2.9 Putting it Together

# Largest 7 files in the current directory find . -ls | sort -nrk 7 | head -7 ls.rec! cwd | (x => x.size -> x) sortBy (-_._1) take 7

35 chars 55 chars

slide-39
SLIDE 39
  • Easy, convenient filesystem ops in Scala!
  • (Almost) as concise as Bash ls! cwd

○ Definitely less typing than java.io/nio

  • Clean, structured data-model

○ Paths. Are. Not. Strings! cwd/'src/'main/"file.txt" ○ Results from commands aren’t strings either

2.10 Ammonite-Ops

slide-40
SLIDE 40

2.11 This begs the question...

Can we use Ammonite-Ops + Scala-REPL as

  • ur default shell?

Let’s try contributing some changes to https: //github.com/lihaoyi/demo

slide-41
SLIDE 41

No

slide-42
SLIDE 42

2.12 No

  • Echo-ed output is unreadable
  • Ctrl-C kills everything; bye bye work!
  • Can’t subprocess out w/o borking JLine
  • http://lihaoyi.github.io/Ammonite/#OtherFixes
slide-43
SLIDE 43

Ammonite-REPL

Re-inventing the Scala REPL

slide-44
SLIDE 44

3.1 Ammonite-REPL

  • Goal

○ You should not need to exit the REPL

  • How often do you need to restart Bash?
slide-45
SLIDE 45

3.2 Using the Ammonite REPL

# Standalone Executable curl -L -o amm https://git.io/v3E3V; chmod +x amm; ./amm // SBT project libraryDependencies += ( "com.lihaoyi" % "ammonite-repl" % "0.4.5" % "test" cross CrossVersion.full ) initialCommands in (Test, console) := """ammonite.repl.Repl.run("")""" // sbt test/console

slide-46
SLIDE 46

Live Demo

Whee!

slide-47
SLIDE 47

3.3 Fun Features

  • Great pretty-printing
  • Syntax-highlighted everything!
  • Ctrl-C Interruptible
  • Live-loading modules from maven central
  • Multi-line editing!
slide-48
SLIDE 48

3.4 Ammonite-REPL

  • A strictly-better Scala REPL
  • Usable in any SBT project
  • Or standalone
slide-49
SLIDE 49

3.5 This begs the question...

Can we use Ammonite-Ops + Ammonite-REPL as our default shell? Let’s try contributing some changes to https: //github.com/lihaoyi/wootjs

slide-50
SLIDE 50

3.6 Ammonite-REPL

  • Scala-REPL is not a plausible systems shell
  • Ammonite-REPL is!
  • (Possibly)
  • You can do real work in it
slide-51
SLIDE 51

3.7 Work In Progress

  • Extensible Autocomplete

○ Already autocomplete properties, names in scope ○ Need to autocomplete filesystem paths ○ Nice to have autocomplete for ivy coordinates, etc.

  • Fetch scaladoc, source to show in-terminal
  • Windows support for Ammonite-REPL

○ Ammonite-Ops already works

slide-52
SLIDE 52

Conclusion

WTF did we just do?

slide-53
SLIDE 53

4.1 Ammonite...

Ammonite-Ops

Really-nice Filesystem Library

Ammonite-REPL

Really-nice Scala REPL

Bash Replacement?

slide-54
SLIDE 54

4.2 Ammonite...

  • Re-implemented much of Bash’s

functionality in Scala

  • Twisted Scala’s syntax into a weird, bash-

like form

  • Re-implemented the Scala REPL to make

this work

slide-55
SLIDE 55

Why?

Did we need to do so many things?

slide-56
SLIDE 56

4.3 Why Not...

  • Make Bash less unsafe?
  • Make Python less verbose?
  • Improve on java.io or java.nio?
slide-57
SLIDE 57

4.4 Space of Possible Systems APIs

Danger Verbosity java.io java.nio Bash Python FP, Inferred Types, ... Ammonite

Plains of Diminished Returns Cliffs of Insanity

slide-58
SLIDE 58

4.5 Problems w/ Scala as your Shell

  • JVM takes time to boot up!

○ 3-4s startup time ○ Not just JVM boot but classloading, etc.

  • 3-4s first command compile

○ 0.2-0.3s compile overhead after warmup

  • Bash takes ~0.004s to boot, Python ~0.03s
  • Jar is 30mb, jar + JVM is >100mb
slide-59
SLIDE 59

4.6 Hopefully free improvements

  • Java 9 w/ modules will help JDK size/speed

○ Can bundle minimal JVM for smaller executable ○ Fewer classes to load on boot

  • Dotty would (hopefully) speed compilation

○ At least it can’t get much slower, right? Right?...

  • Dotty Linker would help overall

○ Should cut down the amount of stuff to load/JIT

slide-60
SLIDE 60

5.0 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ?

Bash, Python, Puppet, Ruby, Vagrant...

DANGER DANGER DANGER DANGER DANGER DANGER

slide-61
SLIDE 61

5.1 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ? Safety

Bash, Python, Puppet, Ruby, Vagrant...

DANGER Safety

slide-62
SLIDE 62

5.2 Application Architecture

Server

Client Client

Database Server

Safety M a y b e S a f e t y ? Safety

Ammonite-Ops Ammonite-REPL

Safety Safety

slide-63
SLIDE 63

5.3 Beyond Bash

  • http://lihaoyi.github.io/Ammonite/
  • "com.lihaoyi" %% "ammonite-ops" % "0.4.5"
  • curl -L -o amm https://git.io/v3E3V; chmod +x amm; ./amm
  • Questions?
slide-64
SLIDE 64

Additional Slides

slide-65
SLIDE 65

2.5 Absolute Paths & RelPaths

case class Path(segments: Seq[String]) case class RelPath(segments: Seq[String], ups: Int)

Absolute Any ..s at the start of the path

slide-66
SLIDE 66

2.6 Constructing Paths

> root / > root/'usr/'bin /usr/bin > 'src/'main src/main > up/up/'src/'main ../../src/main

Paths are constructed using / and...

  • Segments
  • Strings
  • Symbols
  • Builtins
  • root: Path
  • cwd: Path
  • up: RelPath
slide-67
SLIDE 67

2.7 Combining Paths

> val rel = 'src/'main src/main > val wd = root/'Users/'lihaoyi /Users/lihaoyi > wd/rel /Users/lihaoyi/src/main > wd/rel/up /Users/lihaoyi/src

Paths can be stitched together using / Paths are normalized at every step! not /Users/lihaoyi/src/..

slide-68
SLIDE 68

2.8 Invalid Paths

> val rel: RelPath = 'src/'main > val abs: Path = root/'usr/'bin > abs/rel /usr/bin/src/main > rel/abs <console>:15: error: type mismatch; > rel/rel src/main/src/main > abs/abs <console>:14: error: type mismatch;

Combining Paths & RelPaths improperly is a compilation error

slide-69
SLIDE 69

> rel = "???" > abs = "???" > abs + "/" + rel > abs + rel > "/" + abs + rel > "/" + abs + "/" + rel > rel + abs // correct but annoying to write > os.path.join(abs, rel)

2.9 Invalid Paths

> val rel: RelPath = 'src/'main > val abs: Path = root/'usr/'bin > abs/rel /usr/bin/src/main > rel/abs <console>:15: error: type mismatch; > rel/rel src/main/src/main > abs/abs <console>:14: error: type mismatch;