#bullshitlanguage
AnnotationScript
Java annotations with a LISP
Demo time!
│ EqualsVerifier │ jqno.nl │ jqno
#bullshitlanguage
@Autowired
@Bean
@Column(name = "wat")
@Deprecated
@JsonAlias("whynot")
@PostMapping("/endpoint/wtf")
@Test
public void waitwhat() {}
AnnotationScript
is
a LISP
LISP
(define (fizzbuzz x y)
(cond ((eq? (remainder x 15) 0) (display "FizzBuzz\n"))
((eq? (remainder x 3) 0) (display "Fizz\n"))
((eq? (remainder x 5) 0) (display "Buzz\n"))
(else (display x) (display "\n")))
(cond ((< x y) (fizzbuzz (+ x 1) y))
(else ())))
(fizzbuzz 1 100)
(+ x 10)
(+ x 10)
((if (#t) + -) x 10)
(if (< x 0) "a" "b")
(
if
(
<
x
0)
"a"
"b"
)
(
if
(
<
x
0 )
"a"
"b"
)
@Zero("if")
@Zero(list={
@One("<"),
@One("x"),
@One("0")})
@Zero("'a'")
@Zero("'b'")
public static class First {}
Use Peter Norvig’s blog post
Implementation
@Zero("if")
@Zero(list={
@One("<"),
@One("x"),
@One("0")
})
@Zero("'a'")
@Zero("'b'")
@Zero("if") // 'if'
@Zero(list={ // '('
@One("<"), // '<'
@One("x"), // 'x'
@One("0") // '0'
}) // ')'
@Zero("'a'") // 'a'
@Zero("'b'") // 'b'
DONE! 🥳
But …
why so @Weird
?
@Parenthesis("if", @Parenthesis("<", "x", "0"), "'a'", "'b'")
No nesting annotations!
@Open
@Symbol("if")
@Open@Symbol("<")@Symbol("x")@Symbol("0")@Close
@Symbol("'a'")
@Symbol("'b'")
@Close
Sure! I’ll group them for you
Open[] opens = { @Open, @Open };
Symbol[] symbols = { @Symbol("if"), @Symbol("x"), @Symbol("0"),
@Symbol("'a'"), @Symbol("'b'") };
Close[] closes = { @Close, @Close };
public @interface Syntax {} // 👈🏻 smart-ass!
public @interface Open extends Syntax {}
public @interface Symbol extends Syntax {}
public @interface Close extends Syntax {}
Syntax[] code = { @Open, @Symbol("if"), @Open, @Symbol("<"),
@Symbol("x"), @Symbol("0"), @Close, @Symbol("'a'"),
@Symbol("'b'"), @Close };
No extending annotations!
@Zero("if")
@Zero(list={@One("<"), @One("x"), @One("0")})
@Zero("'a'")
@Zero("'b'")
I see no issue with that!
Make as many as you like!
🥱
0
, 'a'
) remain the samedefine
, if
, <
)
are wrapped in Symbol
class(
starts a sub-list)
ends a sub-listList<Object> ast = List.of(
Symbol("if"),
List.of(Symbol('<'), Symbol('x'), 0),
"a",
"b");
(define x 10)
(define x 10)
Is it an Atom?0, 'a'
↓
Return it
Is it a Symbol?if
, <
↓
Look up in Environment
Return it
Is it a List?(< x 0)
↓
Evaluate all elements
Call function
Return result
Interlude
@Zero("begin")
@Zero(list={@One("define"), @One("fizz-buzz"), @One(list={@Two("lambda"),
@Two(list=@Three("n")), @Two(list={@Three("cond"),
@Three(list={@Four("="), @Four(list={@Five("%"), @Five("n"),
@Five("15")}), @Four("0")}), @Three("'fizzbuzz'"),
@Three(list={@Four("="), @Four(list={@Five("%"), @Five("n"),
@Five("3")}), @Four("0")}), @Three("'fizz'"),
@Three(list={@Four("="), @Four(list={@Five("%"), @Five("n"),
@Five("5")}), @Four("0")}), @Three("'buzz'"),
@Three("else"), @Three("n")})})})
@Zero(list={@One("map"), @One("println"), @One(list={@Two("map"),
@Two("fizz-buzz"), @Two(list={@Three("range"),
@Three("1"), @Three("101")})})})
public class FizzBuzz {}
(begin
(define fizz-buzz (lambda (n)
(cond (= (% n 15) 0) 'fizzbuzz')
(cond (= (% n 3) 0) 'fizz')
(cond (= (% n 5) 0) 'buzz')
(else n))
(map println (map fizz-buzz (range 1 101))))
String code = """
(begin
(define fizz-buzz (lambda (n)
(cond (= (% n 15) 0) 'fizzbuzz')
(cond (= (% n 3) 0) 'fizz')
(cond (= (% n 5) 0) 'buzz')
(else n))
(map println (map fizz-buzz (range 1 101))))""";
return code.split(" ");
Taking it
TOO FAR
Unit tests written in Java, not AnnotationScript
AnnotationScript evaluates to regular Java objects
Unit tests: identical!
Unit tests: identical!
What’s next
🤷🏻
Stack overflow errors
Error handling
String.split
Spring integration
🤪
Conclusion
Learn about annotations
Learn about LISP
It was fun
Greenspun’s Tenth Rule:
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
jqno.nl/talks/bullshitlanguage
#bullshitlanguage
image credits: see website