
#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