Last active
September 1, 2024 18:13
-
-
Save rcook/dd4db779353cde36646c3a78736ebb2d to your computer and use it in GitHub Desktop.
Scalaz experimentation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env stack | |
| -- stack --resolver=lts-12.6 script | |
| module Main (main) where | |
| data Expr = | |
| Val Int | |
| | Add Expr Expr | |
| | Sub Expr Expr | |
| | Mul Expr Expr | |
| | Div Expr Expr | |
| eval :: Expr -> Maybe Int | |
| eval (Val x) = pure x | |
| eval (Add ex ey) = (+) <$> eval ex <*> eval ey | |
| eval (Sub ex ey) = (-) <$> eval ex <*> eval ey | |
| eval (Mul ex ey) = (*) <$> eval ex <*> eval ey | |
| eval (Div ex ey) = do | |
| y <- eval ey | |
| if y == 0 | |
| then Nothing | |
| else do | |
| x <- eval ex | |
| pure (x `div` y) | |
| main :: IO () | |
| main = do | |
| -- Divide by 2: displays "Just 7" | |
| print $ eval (Div (Mul (Val 2) (Add (Val 3) (Val 4))) (Val 2)) | |
| -- Divide by 0: displays "Nothing" | |
| print $ eval (Div (Mul (Val 2) (Add (Val 3) (Val 4))) (Val 0)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * High-level commentary: | |
| * This example suggests that Java is, in many ways, an assembly | |
| * language. The resulting Java code is the equivalent Scala or | |
| * Haskell code under a desugaring transform. | |
| */ | |
| package org.rcook; | |
| import java.util.Optional; | |
| import java.util.function.BiFunction; | |
| interface Visitor<T> { | |
| T visit(Val e); | |
| T visit(Add e); | |
| T visit(Sub e); | |
| T visit(Mul e); | |
| T visit(Div e); | |
| } | |
| interface Expr { | |
| <T> T accept(Visitor<T> visitor); | |
| } | |
| final class Val implements Expr { | |
| private final int value; | |
| public Val(int value) { | |
| this.value = value; | |
| } | |
| @Override | |
| public <T> T accept(Visitor<T> visitor) { | |
| return visitor.visit(this); | |
| } | |
| public int getValue() { | |
| return value; | |
| } | |
| } | |
| final class Add implements Expr { | |
| private final Expr left; | |
| private final Expr right; | |
| public Add(Expr left, Expr right) { | |
| this.left = left; | |
| this.right = right; | |
| } | |
| @Override | |
| public <T> T accept(Visitor<T> visitor) { | |
| return visitor.visit(this); | |
| } | |
| public Expr getLeft() { | |
| return left; | |
| } | |
| public Expr getRight() { | |
| return right; | |
| } | |
| } | |
| final class Sub implements Expr { | |
| private final Expr left; | |
| private final Expr right; | |
| public Sub(Expr left, Expr right) { | |
| this.left = left; | |
| this.right = right; | |
| } | |
| @Override | |
| public <T> T accept(final Visitor<T> visitor) { | |
| return visitor.visit(this); | |
| } | |
| public Expr getLeft() { | |
| return left; | |
| } | |
| public Expr getRight() { | |
| return right; | |
| } | |
| } | |
| final class Mul implements Expr { | |
| private final Expr left; | |
| private final Expr right; | |
| public Mul(Expr left, Expr right) { | |
| this.left = left; | |
| this.right = right; | |
| } | |
| @Override | |
| public <T> T accept(Visitor<T> visitor) { | |
| return visitor.visit(this); | |
| } | |
| public Expr getLeft() { | |
| return left; | |
| } | |
| public Expr getRight() { | |
| return right; | |
| } | |
| } | |
| final class Div implements Expr { | |
| private final Expr left; | |
| private final Expr right; | |
| public Div(Expr left, Expr right) { | |
| this.left = left; | |
| this.right = right; | |
| } | |
| @Override | |
| public <T> T accept(Visitor<T> visitor) { | |
| return visitor.visit(this); | |
| } | |
| public Expr getLeft() { | |
| return left; | |
| } | |
| public Expr getRight() { | |
| return right; | |
| } | |
| } | |
| public final class Main { | |
| private static final class EvalVisitor implements Visitor<Optional<Integer>> { | |
| @Override | |
| public Optional<Integer> visit(Val e) { | |
| return Optional.of(e.getValue()); | |
| } | |
| @Override | |
| public Optional<Integer> visit(Add e) { | |
| return binOp(e.getLeft(), e.getRight(), (l, r) -> l + r); | |
| } | |
| @Override | |
| public Optional<Integer> visit(Sub e) { | |
| return binOp(e.getLeft(), e.getRight(), (l, r) -> l - r); | |
| } | |
| @Override | |
| public Optional<Integer> visit(Mul e) { | |
| return binOp(e.getLeft(), e.getRight(), (l, r) -> l * r); | |
| } | |
| @Override | |
| public Optional<Integer> visit(Div e) { | |
| return e | |
| .getRight() | |
| .accept(this) | |
| .filter(r -> r != 0) | |
| .flatMap(r -> e | |
| .getLeft() | |
| .accept(this) | |
| .flatMap(l -> Optional.of(l / r))); | |
| } | |
| private Optional<Integer> binOp( | |
| Expr left, | |
| Expr right, | |
| BiFunction<Integer, Integer, Integer> op) { | |
| return left | |
| .accept(this) | |
| .flatMap(l -> right | |
| .accept(this) | |
| .flatMap(r -> Optional.of(op.apply(l, r)))); | |
| } | |
| } | |
| // Use Optional<Integer> since OptionalInt doesn't support flatMap etc. | |
| private static Optional<Integer> eval(Expr e) { | |
| EvalVisitor v = new EvalVisitor(); | |
| return e.accept(v); | |
| } | |
| public static void main(String[] args) { | |
| // Divide by 2: outputs "Optional[7]" | |
| System.out.println(eval( | |
| new Div( | |
| new Mul( | |
| new Val(2), | |
| new Add( | |
| new Val(3), | |
| new Val(4))), | |
| new Val(2)))); | |
| // Divide by 0: outputs "Optional.empty" | |
| System.out.println(eval( | |
| new Div( | |
| new Mul( | |
| new Val(2), | |
| new Add( | |
| new Val(3), | |
| new Val(4))), | |
| new Val(0)))); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| open Core.Option | |
| open Printf | |
| type expr = | |
| | Val of int | |
| | Add of expr * expr | |
| | Sub of expr * expr | |
| | Mul of expr * expr | |
| | Div of expr * expr | |
| let rec to_sexpr = function | |
| | Val x -> string_of_int x | |
| | Add (left, right) -> | |
| sprintf "(add %s %s)" (to_sexpr left) (to_sexpr right) | |
| | Sub (left, right) -> | |
| sprintf "(sub %s %s)" (to_sexpr left) (to_sexpr right) | |
| | Mul (left, right) -> | |
| sprintf "(mul %s %s)" (to_sexpr left) (to_sexpr right) | |
| | Div (left, right) -> | |
| sprintf "(div %s %s)" (to_sexpr left) (to_sexpr right) | |
| let rec eval = function | |
| | Val x -> Some x | |
| | Add (left, right) -> binOp left right (+) | |
| | Sub (left, right) -> binOp left right (-) | |
| | Mul (left, right) -> binOp left right ( * ) | |
| | Div (left, right) -> binOpM left right (fun l r -> | |
| if r = 0 | |
| then None | |
| else Some (l / r)) | |
| and binOp left right f = map2 (eval left) (eval right) f | |
| and binOpM left right f = | |
| eval left >>= fun l -> | |
| eval right >>= fun r -> | |
| f l r | |
| let string_of_option f o = match o with | |
| | None -> "None" | |
| | Some x -> sprintf "Some %s" (f x) | |
| let () = | |
| (* Divide by 2 *) | |
| let e = Div (Mul (Val 2, Add (Val 3, Val 4)), Val 2) in | |
| print_endline (to_sexpr e); | |
| print_endline (string_of_option string_of_int (eval e)); | |
| (* Divide by 0 *) | |
| let e = Div (Mul (Val 2, Add (Val 3, Val 4)), Val 0) in | |
| print_endline (to_sexpr e); | |
| print_endline (string_of_option string_of_int (eval e)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mod expr { | |
| use Expr::*; | |
| #[derive(Debug)] | |
| pub enum Expr { | |
| Val(i32), | |
| Add(Box<Expr>, Box<Expr>), | |
| Sub(Box<Expr>, Box<Expr>), | |
| Mul(Box<Expr>, Box<Expr>), | |
| Div(Box<Expr>, Box<Expr>), | |
| } | |
| pub fn val(value: i32) -> Box<Expr> { | |
| Box::new(Val(value)) | |
| } | |
| pub fn add(left: Box<Expr>, right: Box<Expr>) -> Box<Expr> { | |
| Box::new(Add(left, right)) | |
| } | |
| #[allow(dead_code)] | |
| pub fn sub(left: Box<Expr>, right: Box<Expr>) -> Box<Expr> { | |
| Box::new(Sub(left, right)) | |
| } | |
| pub fn mul(left: Box<Expr>, right: Box<Expr>) -> Box<Expr> { | |
| Box::new(Mul(left, right)) | |
| } | |
| pub fn div(left: Box<Expr>, right: Box<Expr>) -> Box<Expr> { | |
| Box::new(Div(left, right)) | |
| } | |
| } | |
| fn eval(e: Box<expr::Expr>) -> Option<i32> { | |
| use expr::Expr::*; | |
| match *e { | |
| Val(value) => Some(value), | |
| Add(left, right) => { | |
| let l = eval(left)?; | |
| let r = eval(right)?; | |
| Some(l + r) | |
| } | |
| Sub(left, right) => { | |
| let l = eval(left)?; | |
| let r = eval(right)?; | |
| Some(l - r) | |
| } | |
| Mul(left, right) => { | |
| let l = eval(left)?; | |
| let r = eval(right)?; | |
| Some(l * r) | |
| } | |
| Div(left, right) => { | |
| let r = eval(right)?; | |
| if r == 0 { return None } | |
| let l = eval(left)?; | |
| Some(l / r) | |
| } | |
| } | |
| } | |
| fn main() { | |
| use expr::*; | |
| { | |
| let e = div(mul(val(2), add(val(3), val(4))), val(2)); | |
| println!("e={:?}", eval(e)) // Divide by 2: displays "Some(7)" | |
| } | |
| { | |
| let e = div(mul(val(2), add(val(3), val(4))), val(0)); | |
| println!("e={:?}", eval(e)) // Divide by 0: displays "None" | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import scalaz._ | |
| import std.option._ | |
| sealed trait Expr | |
| case class Val(x: Int) extends Expr | |
| case class Add(x: Expr, y: Expr) extends Expr | |
| case class Sub(x: Expr, y: Expr) extends Expr | |
| case class Mul(x: Expr, y: Expr) extends Expr | |
| case class Div(x: Expr, y: Expr) extends Expr | |
| object Main { | |
| private def eval(e: Expr): Option[Int] = | |
| e match { | |
| case Val(x) => some(x) | |
| case Add(ex, ey) => binOp(ex, ey)(_ + _) | |
| case Sub(ex, ey) => binOp(ex, ey)(_ - _) | |
| case Mul(ex, ey) => binOp(ex, ey)(_ * _) | |
| case Div(ex, ey) => | |
| eval(ey) | |
| .flatMap(y => | |
| if (y == 0) | |
| none | |
| else | |
| eval(ex).map(_ / y)) | |
| /* | |
| // Equivalent for-comprehension | |
| case Div(ex, ey) => for { | |
| y <- eval(ey) | |
| if (y != 0) | |
| x <- eval(ex) | |
| } yield (x / y) | |
| */ | |
| } | |
| private def binOp(ex: Expr, ey: Expr): ((Int, Int) => Int) => Option[Int] = { | |
| Apply[Option].apply2(eval(ex), eval(ey)) | |
| } | |
| def main(args: Array[String]): Unit = { | |
| // Divide by 2: displays "Some(7)" | |
| println(eval(Div(Mul(Val(2), Add(Val(3), Val(4))), Val(2)))) | |
| // Divide by 0: displays "None" | |
| println(eval(Div(Mul(Val(2), Add(Val(3), Val(4))), Val(0)))) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| The MIT License (MIT) | |
| Copyright (c) 2019 Richard Cook | |
| Permission is hereby granted, free of charge, to any person obtaining a copy of | |
| this software and associated documentation files (the "Software"), to deal in | |
| the Software without restriction, including without limitation the rights to | |
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
| the Software, and to permit persons to whom the Software is furnished to do so, | |
| subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment