class: center, middle, inverse # FlowRun Building Visual Logic that Actually Flows
by Sakib Hadžiavdić
--- # Who am I? ### Sakib Hadžiavdić - Based in Sarajevo, Bosnia 🇧🇦 - Software Engineer @ [SaCode](https://sacode.dev) - Doing OSS work: FlowRun, Hepek, Sharaf... https://github.com/sake92 --- background-image: url(content/flowrun_vs_hand.png) --- # Goals - free! - web-based 📱 - offline ready 📶 - snappy, easy to use UI - debugger 🐛 - code generator --- # Prior art - https://scratch.mit.edu/ - http://flowgorithm.org/ (Windows only) - https://raptor.martincarlisle.com/ (Windows only) --- # Demo time! [https://flowrun.io/scratchpad](flowrun.io/scratchpad) --- background-image: url(content/visit_stats.png) # Stats --- background-image: url(content/visit_stats_tech.png) # Stats Tech --- background-image: url(content/visit_stats_geo.png) --- class: center, middle, inverse # FlowRun.io Architecture --- background-image: url(content/flowrun.io_architecture.png) # FlowRun.io Architecture --- # Tech Stack ## Backend - Scala 3 - Sharaf (my mini web framework) - Postgres ## Editor - Scala.js --- # Deployment - GitHub Actions - Docker Compose - one-click deploy --- class: center, middle, inverse # Scala a programming language that scales with you --- # Scala: TLDR - the **best programming language** (JVM, JS, WASM, Native) - FP - immutability, ADTs - pattern matching - type classes - OOP - classes, objects etc - scala-cli for scripting --- # Sharaf 🔩 - minimalistic framework in Scala 3 - "direct style" (no async complexities..) - "batteries included" - based on Undertow (but supports Helidon too!) - https://sake92.github.io/sharaf/ --- ## Sharaf: Hello World ```scala //> using scala 3.7.0 //> using dep ba.sake::sharaf-undertow:0.13.0 import ba.sake.sharaf.* import ba.sake.sharaf.undertow.UndertowSharafServer val routes = Routes { case GET -> Path("hello", name) => Response.withBody(s"Hello $name") } UndertowSharafServer("localhost", 8181, routes).start() println(s"Server started at http://localhost:8181") ``` --- ## Sharaf: benefits ```scala case GET -> Path("files", segments*) => ??? case GET -> Path("files", "abc.txt") => ??? // Unreachable case // typesafe extract path params, DRY case GET -> Path("cars", param[Int](carId)) => ??? // also enums, regexes... ``` --- ## Sharaf: Serving HTML - pure scala, safe interpolation! - no files, no template language ```scala val routes = Routes { case GET -> Path() => Response.withBody(IndexView) } def IndexView = html""" <!DOCTYPE html> <html lang="en"> <body> <div> <p>Welcome!</p> <a href="/hello/Bob">Hello world</a> </div> </body> </html> """ ``` --- ## Query Params ```scala case GET -> Path("query-params") => case class SearchParams(q: String, perPage: Int) derives QueryStringRW val qp = Request.current.queryParams[SearchParams] Response.withBody(s"Got query params: ${qp}") ``` ## Forms ```scala case POST -> Path("handle-form") => case class ContactUsForm(fullName: String, email: String) derives FormDataRW val formData = Request.current.bodyForm[ContactUsForm] Response.withBody(s"Got form data: ${formData}") ``` ## JSON ```scala case POST -> Path("handle-json") => case class MyRequest(fullName: String, email: String) derives JsonRW val jsonData = Request.current.bodyForm[MyRequest] Response.withBody(s"Got JSON data: ${jsonData}") ``` --- # Scala.js Why? - share code between backend and frontend - type safety - FP style works well for writing compilers/interpreters - seamless interop with JS libraries (e.g. D3, Graphviz etc) --- # Scala.js ```scala sealed trait Statement derives JsonRW object Statement: case class Begin(id: String) extends Statement derives JsonRW: override def label = "Begin" case class Return( id: String, maybeValue: Option[String] = None ) extends Statement derives JsonRW: override def duplicated = copy(id = AST.newId) override def label = maybeValue.map(e => s"return $e").getOrElse("return") ... val statements: List[Statement] = List(Statement.Begin("1"), Statement.Return("2", Some("42"))) statements.map { case s: Statement.Begin => ... case s: Statement.Return => ... } ``` --- # Challenges - parsing expresssions - interpreting code - debug mode - error handling and reporting - mixing visual blocks and inline expressions -- - **layout algorithm** (the _hardest_ one!) --- # Lessons learned - start simple, iterate (crawl, walk, run) - publish early - user feedback is essential - try stuff, dont be afraid - write tests! - work hard to keep things simple!! - UX is hard - marketing is **really** hard (esp. for us "techies") --- # What's next? - improve editor UX - more video tutorials - polish integration with Google Classroom --- # Resources - https://flowrun.io/tutorials - free ebook https://leanpub.com/learnprogrammingwithflowrun - https://scala-lang.org/ - https://blog.sake.ba/en/programming/