jaspervdj / turnstyle

Turnstyle is a graphical esoteric programming language based on lambda calculus
https://jaspervdj.be/turnstyle/
GNU General Public License v2.0
76 stars 2 forks source link

Feedback/Support request #10

Open JanEricNitschke opened 1 month ago

JanEricNitschke commented 1 month ago

This doesnt feel like the correct place to put this but i dont know if there is a more informal way to ask this:

So i am trying to write a game of tictactoe in turnstyle. So far i have tried to map it out in lambda calculus given the fact that recursion is more easily possible because lambda can have a "name (as in place in the picture).

Would love if you could have a look to see if it is in the right direction and could work like that.

The next step then would be to turn that into a turnstyle picture. Do you have a tool you use and a rough workflow you followed to produce the examples?

JanEricNitschke commented 1 month ago

This is my idea of howto possibly do that.

# ruff: noqa: E741, E731

def in_num(k):
    def inner(l):
        try:
            x = int(input())
            return k(x)
        except:
            return l

    return inner

def in_char(k):
    def inner(l):
        try:
            x = ord(input())
            return k(x)
        except:
            return l

    return inner

def out_number(x):
    def inner(k):
        print(x, end="")
        return k

    return inner

def out_char(x):
    def inner(k):
        print(chr(x), end="")
        return k

    return inner

def num_add(x):
    def inner(y):
        return x + y

    return inner

def num_sub(x):
    def inner(y):
        return x - y

    return inner

def num_mul(x):
    def inner(y):
        return x * y

    return inner

def num_div(x):
    def inner(y):
        return x // y

    return inner

def num_mod(x):
    def inner(y):
        return x % y

    return inner

def cmp_eq(x):
    def inner1(y):
        def inner2(t):
            def inner3(f):
                return t if x == y else f

            return inner3

        return inner2

    return inner1

def cmp_lt(x):
    def inner1(y):
        def inner2(t):
            def inner3(f):
                return t if x < y else f

            return inner3

        return inner2

    return inner1

def cmp_gt(x):
    def inner1(y):
        def inner2(t):
            def inner3(f):
                return t if x > y else f

            return inner3

        return inner2

    return inner1

def cmp_lte(x):
    def inner1(y):
        def inner2(t):
            def inner3(f):
                return t if x <= y else f

            return inner3

        return inner2

    return inner1

def cmp_gte(x):
    def inner1(y):
        def inner2(t):
            def inner3(f):
                return t if x >= y else f

            return inner3

        return inner2

    return inner1

def inexact_sqrt(x):
    return x**0.5

PAIR = lambda a: lambda b: lambda f: f(a)(b)
FIRST = lambda a: lambda b: a
SECOND = lambda a: lambda b: b

NIL = None

LIST = lambda a: lambda b: lambda f: f(a)(b)

stuff = LIST(0)(LIST(1)(LIST(2)(LIST(3)(NIL))))

IS_ZERO = lambda n: cmp_eq(n)(0)

# Recursive GET_NTH function
# The proper version doesnt work in python because it gets evaluated eagerly.
# GET_NTH = lambda l: lambda n: IS_ZERO(n)(l(FIRST))(GET_NTH(l(SECOND))(num_sub(n)(1)))
def GET_NTH(l):
    def inner(n):
        if n == 0:
            return l(FIRST)
        else:
            return GET_NTH(l(SECOND))(num_sub(n)(1))

    return inner

# print(GET_NTH(0)(stuff))

# Recursive SET_NTH function
# SET_NTH = lambda l: lambda v: lambda n: IS_ZERO(n)(LIST(v)(LIST(SECOND)))(LIST(l(FIRST))(SET_NTH(l(SECOND))(v)(num_sub(n)(1))))
def SET_NTH(l):
    def inner(v):
        def inner2(n):
            if n == 0:
                return LIST(v)(LIST(l(SECOND)))
            else:
                return LIST(l(FIRST))(SET_NTH(l(SECOND))(v)(num_sub(n)(1)))

        return inner2

    return inner

# print(GET_NTH(2)(stuff))
# print(GET_NTH(2)(SET_NTH(2)(5)(stuff)))
# print(GET_NTH(1)(SET_NTH(2)(5)(stuff)))
# print(GET_NTH(1)(SET_NTH(2)(5)(stuff)))

# 012
# 345
# 678

EMPTY = 45  # Empty cell
PLAYER1 = 88  # Player 1 (X)
PLAYER2 = 79  # Player 2 (O)
BOARD = LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(LIST(EMPTY)(NIL)))))))))

def condition_check(board):
    def inner1(x):
        def inner2(y):
            def inner3(z):
                def inner4(player):
                    return cmp_eq(GET_NTH(board)(x))(player)(cmp_eq(GET_NTH(board)(y))(player)(cmp_eq(GET_NTH(board)(z))(player)(1)(0))(0))(0)
                return inner4
            return inner3
        return inner2
    return inner1

def print_win(player):
    def inner(f):
        return out_char(87)(out_char(105)(out_char(110)(out_char(32)(out_char(102)(out_char(111)(out_char(114)(out_char(32)(out_char(player)(out_char(33)(out_char(10)(f)))))))))))
    return inner

def win_check(board):
    def inner(player):
        return cmp_eq(condition_check(board)(0)(1)(2)(player))(1)(print_win(player)(1))(cmp_eq(condition_check(board)(3)(4)(5)(player))(1)(print_win(player)(1))(cmp_eq(condition_check(board)(6)(7)(8)(player))(1)(print_win(player)(1))(
            cmp_eq(condition_check(board)(0)(3)(6)(player))(1)(print_win(player)(1))(cmp_eq(condition_check(board)(1)(4)(7)(player))(1)(print_win(player)(1))(cmp_eq(condition_check(board)(2)(5)(8)(player))(1)(print_win(player)(1))(
                cmp_eq(condition_check(board)(0)(4)(8)(player))(1)(print_win(player)(1))(cmp_eq(condition_check(board)(2)(4)(6)(player))(1)(print_win(player)(1))(0))
            )))
        )))
    return inner

# print(condition_check(BOARD)(0)(1)(2)(PLAYER1))
# print(win_check(BOARD)(EMPTY))
def print_draw(f):
    return out_char(68)(out_char(114)(out_char(97)(out_char(119)(out_char(10)(f)))))

def draw_check(board):
    return cmp_eq(GET_NTH(board)(0))(EMPTY)(0)(cmp_eq(GET_NTH(board)(1))(EMPTY)(0)(cmp_eq(GET_NTH(board)(2))(EMPTY)(0)(cmp_eq(GET_NTH(board)(3))(EMPTY)(0)(cmp_eq(GET_NTH(board)(4))(EMPTY)(0)(cmp_eq(GET_NTH(board)(5))(EMPTY)(0)(cmp_eq(GET_NTH(board)(6))(EMPTY)(0)(cmp_eq(GET_NTH(board)(7))(EMPTY)(0)(cmp_eq(GET_NTH(board)(8))(EMPTY)(0)(print_draw(1))))))))))

# print(draw_check(BOARD))

def show_board(board):
    def inner(f):
        return out_char(GET_NTH(board)(0))(out_char(GET_NTH(board)(1))(out_char(GET_NTH(board)(2))(out_char(10)(out_char(GET_NTH(board)(3))(out_char(GET_NTH(board)(4))(out_char(GET_NTH(board)(5))(out_char(10)(out_char(GET_NTH(board)(6))(out_char(GET_NTH(board)(7))(out_char(GET_NTH(board)(8))(out_char(10)(out_char(10)(f)))))))))))))
    return inner

# print(show_board(NIL)(BOARD))

def game_over(board):
    def inner(player):
        return cmp_eq(win_check(board)(player))(1)(1)(draw_check(board))
    return inner

def take_player_input(k):
    def inner(l):
        return out_char(73)(out_char(110)(out_char(112)(out_char(117)(out_char(116)(out_char(58)(out_char(10)(in_num(k)(l))))))))
    return inner

def swap_player(player):
    return cmp_eq(player)(PLAYER1)(PLAYER2)(PLAYER1)

def game_loop1(board):
    def inner(player):
        return game_loop2(take_player_input(show_board(board)(SET_NTH(board)(player)))(1))(player)   
    return inner

# should be:
# lambda new_board: lambda: player: cmp_eq(game_over(new_board)(player))(1)(0)(game_loop1(new_board)(swap_player(player)))
def game_loop2(new_board):
    def inner(player):
        if game_over(new_board)(player) == 1:
            return 0
        else:
            return game_loop1(new_board)(swap_player(player))
    return inner

game_loop1(BOARD)(PLAYER1)
jaspervdj commented 1 month ago

I think Tic-tac-toe is quite a challenge!

Too be honest, I haven't really figured out the best way to write programs, and I am still researching this a bit. The examples were hand-drawn in GIMP, which is very tedious (but that's why they are so simple, tic-tac-toe is a lot more complex).

There is a VERY rudimentary compiler that you can use. It turns lambda calculus + let expressions into pictures.

If you have the file rot13.txt:

LET y = λf. (λx. f (x x)) (λx. f (x x)) IN # y combinator
LET char_a = (num_add (num_mul 10 9) 7) IN # characters
LET char_z = (num_add char_a 25) IN
LET and = λp q. p q p IN # church-encoded and for bools
LET is_alpha = λn. and (cmp_gte n char_a) (cmp_lte n char_z) IN
LET rot13 = λn. (is_alpha n)
        (num_add char_a (num_mod (num_add 13 (num_sub n char_a)) 26))
        n IN
y (λrec. in_char (λn. out_char (rot13 n) rec) (num_sub 1 1))

You can compile this using turnstyle compile rot13.txt, which will produce an image a.png.

This image will look very bad since the compiler isn't great. You can use compile -O to get the compiler to try and optimize the result (i.e. make the image smaller) but I haven't spent that much time on it, and it is very slow.

But it should work to at least validate the program a little bit. I will try to get some documentation on the compiler written up, but feel free to ping me if you have any questions.

JanEricNitschke commented 1 month ago

Ah, nice. I had seen the compile option in the code but didnt realize it could handle let expressions. Will definitely have a look.

JanEricNitschke commented 1 month ago

Ah, another question. This compiler doesnt supprt direct recursion, right?

Like: GET_NTH = lambda l: lambda n: IS_ZERO(n)(l(FIRST))(GET_NTH(l(SECOND))(num_sub(n)(1)))

I have to use the y combinator for that?

jaspervdj commented 1 month ago

Yes, that's correct, you need to use a fixed-point combinator. Maybe we can extend the compiler to do it automatically, but I'm not sure if we should extend LET or introduce LETREC.

JanEricNitschke commented 1 month ago

I tried to do a first translation to the compiler syntax.

Two main points, the first is that i still have GET and SET on lists as recursive and the last is that i am unsure how to handle the two game loop functions.

I couldnt do it in one because i need the "new board" in two places but i also dont know how to do this mutual recursion here.

let LIST = λa. λb. λf. f(a)(b) IN
let FIRST = λa. λb. a IN
let SECOND = λa. λb. b IN
let IS_ZERO = λn. cmp_eq(n)(0) IN
let EMPTY = 45 IN # Empty cell (-)
let PLAYER1 = 88 IN # Player 1 (X)
let PLAYER2 = 79 IN # Player 2 (O)
let NIL = 99 IN
let BOARD = ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) ((LIST EMPTY) NIL))))))))) IN
# These are currently still normally recursive.
# Need to do with Y-Combinator
let GET_NTH = λl. λn. (((IS_ZERO n) (l FIRST)) ((GET_NTH (l SECOND)) ((num_sub n) 1))) IN
let SET_NTH = λl. λv. λn. (((IS_ZERO n) ((LIST v) (l SECOND))) (((SET_NTH (l SECOND)) v) ((num_sub n) 1))) IN IN
let CONDITION_CHECK = λb. λx. λy. λz. λp. ((((cmp_eq ((GET_NTH b) x)) p) ((((cmp_eq ((GET_NTH b) y)) p) ((((cmp_eq ((GET_NTH z) x)) p) ) 0)) 0)) 0) IN
let PRINT_WINNER = λp. λf. ((out_char 87) ((out_char 105) ((out_char 110) ((out_char 32) ((out_char 102) ((out_char 111) ((out_char 114) ((out_char 32) ((out_char p) ((out_char 33) ((out_char 10) f))))))))))) IN
let WIN_CHECK =  λb. λp. ((((cpm_eq (((((CONDITION_CHECK b) 0) 1) 2) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 3) 4) 5) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 6) 7) 8) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 0) 3) 6) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 1) 4) 7) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 2) 5) 8) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 0) 4) 8) p)) 1) ((PRINT_WINNER p) 1)) ((((cpm_eq (((((CONDITION_CHECK b) 2) 4) 6) p)) 1) ((PRINT_WINNER p) 1)) 0)))))))) IN
let PRINT_DRAW = λf. ((out_char 68) ((out_char 114) ((out_char 97) ((out_char 119) ((out_char 10) f)))))
let DRAW_CHECK = λb. ((((cmp_eq ((GET_NTH b) 0)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 1)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 2)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 3)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 4)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 5)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 6)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 7)) EMPTY) 0) ((((cmp_eq ((GET_NTH b) 8)) EMPTY) 0) 1))))))))) IN
let SHOW_BOARD = λb. λf. ((out_char ((GET_NTH b) 0)) ((out_char ((GET_NTH b) 1)) ((out_char ((GET_NTH b) 2)) ((out_char 10) ((out_char ((GET_NTH b) 3)) ((out_char ((GET_NTH b) 4)) ((out_char ((GET_NTH b) 5)) ((out_char 10) ((out_char ((GET_NTH b) 6)) ((out_char ((GET_NTH b) 7)) ((out_char ((GET_NTH b) 8)) ((out_char 10) f)))))))))))) IN
let GAME_OVER = λb. λp. ((((cmp_eq ((WIN_CHECK b) p)) 1) 1) (DRAW_CHECK b)) IN
let TAKE_PLAYER_INPUT = λk. λl. ((out_char 73) ((out_char 110) ((out_char 112) ((out_char 117) ((out_char 116) ((out_char 58) ((out_char 10) ((in_num k) l)))))))) IN
let SWAP_PLAYER =  λp. ((((cmp_eq p) PLAYER1) PLAYER2) PLAYER1) IN
# How to handle this?
# Couldnt get it in one function and i dont know how to do this mutual recursion
let GAME_LOOP_1 = λb. λp. ((GAME_LOOP_2 ((TAKE_PLAYER_INPUT ((SHOW_BOARD b) ((SET_NTH b) p))) 1)) p) IN
let GAME_LOOP_2 = λnb. λp. ((((cmp_eq ((GAME_OVER nb) p)) 1) 0) ((GAME_LOOP_1 nb) (SWAP_PLAYER p))) IN
((GAME_LOOP_1  BOARD) PLAYER1)
jaspervdj commented 1 month ago

This is probably a good approach for mutual recursion: https://okmij.org/ftp/Computation/fixed-point-combinators.html

jaspervdj commented 1 month ago

This is an example where the clause is passed explicitly, which works just as well:

# Preliminary
LET y     = λf. (λx. f (x x)) (λx. f (x x)) IN
LET zero  = num_sub 1 1 IN
LET true  = λp q. p IN
LET false = λp q. q IN

# Mutual recursion example
LET mutrec_is_even = 100 IN
LET mutrec_is_odd  = 101 IN
LET mutrec = y (λrec mutrec_is n.
        LET is_even = rec mutrec_is_even IN
        LET is_odd  = rec mutrec_is_odd  IN
        cmp_eq mutrec_is mutrec_is_even
            (cmp_eq n zero true  (is_odd  (num_sub n 1)))
            (cmp_eq n zero false (is_even (num_sub n 1)))) IN

# Main: print 1 on even numbers, 0 on odd numbers.
y (λrec. in_num (λn. out_num (mutrec mutrec_is_even n 1 zero) rec) zero)
JanEricNitschke commented 1 month ago

I currently don't quite get how your example works in detail.

Maybe I'll do if I can run it, but that will only be on Sunday. Would love if you could split/explain it a bit more.

I am specifically a bit confused about the 100 and 101.

Edit: Ah, i think i got it. Instead of actually mutually recursing you just have one function that has a parameter that determines which of the 2 original functions it is doing right now

JanEricNitschke commented 1 month ago

I have changed it now to this:

LET list = λa. λb. λf. f(a)(b) IN
LET first = λa. λb. a IN
LET second = λa. λb. b IN
LET zero = ((num_sub 1) 1) IN
LET is_zero = λn. cmp_eq(n)(zero) IN
LET empty = 45 IN # Empty cell (-)
LET player1 = 88 IN # Player 1 (X)
LET player2 = 79 IN # Player 2 (O)
LET nil = 99 IN
LET board = ((list empty) ((list empty) ((list empty) ((list empty) ((list empty) ((list empty) ((list empty) ((list empty) ((list empty) nil))))))))) IN
LET y = λf. (λx. f (x x)) (λx. f (x x)) IN # y combinator
LET get_helper = λrec. λl. λn. (((is_zero n) (l first)) ((rec (l second)) ((num_sub n) 1))) IN
LET set_helper = λrec. λl. λv. λn. (((is_zero n) ((list v) (l second))) (((rec (l second)) v) ((num_sub n) 1))) IN
LET get_nth = (y get_helper) IN
LET set_nth = (y set_helper) IN
LET condition_check = λb. λx. λy. λz. λp. ((((cmp_eq ((get_nth b) x)) p) ((((cmp_eq ((get_nth b) y)) p) ((((cmp_eq ((get_nth z) x)) p) ) zero)) zero)) zero) IN
LET print_winner = λp. λf. ((out_char 87) ((out_char 105) ((out_char 110) ((out_char 32) ((out_char 102) ((out_char 111) ((out_char 114) ((out_char 32) ((out_char p) ((out_char 33) ((out_char 10) f))))))))))) IN
LET win_check =  λb. λp. ((((cmp_eq (((((condition_check b) zero) 1) 2) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) 3) 4) 5) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) 6) 7) 8) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) zero) 3) 6) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) 1) 4) 7) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) 2) 5) 8) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) zero) 4) 8) p)) 1) ((print_winner p) 1)) ((((cmp_eq (((((condition_check b) 2) 4) 6) p)) 1) ((print_winner p) 1)) zero)))))))) IN
LET print_draw = λf. ((out_char 68) ((out_char 114) ((out_char 97) ((out_char 119) ((out_char 10) f))))) IN
LET draw_check = λb. ((((cmp_eq ((get_nth b) zero)) empty) zero) ((((cmp_eq ((get_nth b) 1)) empty) zero) ((((cmp_eq ((get_nth b) 2)) empty) zero) ((((cmp_eq ((get_nth b) 3)) empty) zero) ((((cmp_eq ((get_nth b) 4)) empty) zero) ((((cmp_eq ((get_nth b) 5)) empty) zero) ((((cmp_eq ((get_nth b) 6)) empty) zero) ((((cmp_eq ((get_nth b) 7)) empty) zero) ((((cmp_eq ((get_nth b) 8)) empty) zero) (print_draw 1)))))))))) IN
LET show_board = λb. λf. ((out_char ((get_nth b) zero)) ((out_char ((get_nth b) 1)) ((out_char ((get_nth b) 2)) ((out_char 10) ((out_char ((get_nth b) 3)) ((out_char ((get_nth b) 4)) ((out_char ((get_nth b) 5)) ((out_char 10) ((out_char ((get_nth b) 6)) ((out_char ((get_nth b) 7)) ((out_char ((get_nth b) 8)) ((out_char 10) f)))))))))))) IN
LET game_over = λb. λp. ((((cmp_eq ((win_check b) p)) 1) 1) (draw_check b)) IN
LET take_player_input = λk. λl. ((out_char 73) ((out_char 110) ((out_char 112) ((out_char 117) ((out_char 116) ((out_char 58) ((out_char 10) ((in_num k) l)))))))) IN
LET swap_player =  λp. ((((cmp_eq p) player1) player2) player1) IN
LET loop_variant_1 = 100 IN
LET loop_variant_2 = 101 IN
LET game_loop_helper = λrec. λv. λb. λp.
    LET loop_1 = rec loop_variant_1 IN
    LET loop_2 = rec loop_variant_2 IN
    cmp_eq v loop_variant_1
         ((loop_2 ((take_player_input ((show_board b) ((set_nth b) p))) 1)) p)
         ((((cmp_eq ((game_over b) p)) 1) zero) ((loop_1 b) (swap_player p))) IN
LET game_loop = (y game_loop_helper) IN
(((game_loop loop_variant_1) board) player1)

But i am actually getting an error when running compile:

turnstyle: Prelude.head: empty list
CallStack (from HasCallStack):
  error, called at libraries\ghc-internal\src\GHC\Internal\List.hs:2030:3 in ghc-internal:GHC.Internal.List
  errorEmptyList, called at libraries\ghc-internal\src\GHC\Internal\List.hs:96:11 in ghc-internal:GHC.Internal.List
  badHead, called at libraries\ghc-internal\src\GHC\Internal\List.hs:102:48 in ghc-internal:GHC.Internal.List
  head, called at hs/lib\Turnstyle\Compile\Solve.hs:84:25 in turnstyle-0.1.0.0-inplace:Turnstyle.Compile.Solve
jaspervdj commented 1 month ago

Thanks for providing the source. I hadn't tried files this big before so it was running out of colors. I've fixed this in c0c4f73.

JanEricNitschke commented 1 month ago

Nice, thanks! Now it compiles. But i do seem to have made a mistake somewhere, i will have a look at that.

But one more thing. Right now it seems that both my tictactoe as well as rot13 you posted or the other examples have buffered input + output. So for my tictactoe it currently looks like:

 turnstyle run tictactoe.png
0
1
2
3
(λa. (λb. (λc. (λd. (λe. (λf. (λg. (λh. (λi. (λj. (λk. (λl. (λm. (λn. (λo. (λp. (λq. (λr. (λs. (λt. (λu. (λv. (λw. (λx. (λy. (λz. (λ{. (λ|. ((| y) j) g) (k {)) (λ}. λ{. λ~. λ. (λ|. (λ€. (((cmp_eq {) y) ((€ ((w ((u ~) ((o ~) ))) 1)) )) ((((cmp_eq ((v ~) )) 1) d) ((| ~) (x )))) (} z)) (} y))) 101) 100) (λ{. (((cmp_eq {) g) h) g)) (λw. λ{. (out_char 73) ((out_char 110) ((out_char 112) ((out_char 117) ((out_char 116) ((out_char 58) ((out_char 10) ((in_num w) {))))))))) (λ{. λ€. (((cmp_eq ((r {) €)) 1) 1) (t {))) (λu. λ{. (out_char ((n u) d)) ((out_char ((n u) 1)) ((out_char ((n u) 2)) ((out_char 10) ((out_char ((n u) 3)) ((out_char ((n u) 4)) ((out_char ((n u) 5)) ((out_char 10) ((out_char ((n u) 6)) ((out_char ((n u) 7)) ((out_char ((n u) 8)) ((out_char 10) {))))))))))))) (λu. (((cmp_eq ((n u) d)) f) d) ((((cmp_eq ((n u) 1)) f) d) ((((cmp_eq ((n u) 2)) f) d) ((((cmp_eq ((n u) 3)) f) d) ((((cmp_eq ((n u) 4)) f) d) ((((cmp_eq ((n u) 5)) f) d) ((((cmp_eq ((n u) 6)) f) d) ((((cmp_eq ((n u) 7)) f) d) ((((cmp_eq ((n u) 8)) f) d) (s 1))))))))))) (λ{. (out_char 68) ((out_char 114) ((out_char 97) ((out_char 119) ((out_char 10) {)))))) (λ{. λu. (((cmp_eq (((((p {) d) 1) 2) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) 3) 4) 5) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) 6) 7) 8) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) d) 3) 6) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) 1) 4) 7) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) 2) 5) 8) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) d) 4) 8) u)) 1) ((q u) 1)) ((((cmp_eq (((((p {) 2) 4) 6) u)) 1) ((q u) 1)) d))))))))) (λ{. λw. (out_char 87) ((out_char 105) ((out_char 110) ((out_char 32) ((out_char 102) ((out_char 111) ((out_char 114) ((out_char 32) ((out_char {) ((out_char 33) ((out_char 10) w)))))))))))) (λw. λ€. λk. λ{. λu. (((cmp_eq ((n w) €)) u) ((((cmp_eq ((n w) k)) u) (((cmp_eq ((n {) €)) u) d)) d)) d)) (k m)) (k l)) (λu. λm. λ{. λw. ((e w) ((a {) (m c))) (((u (m c)) {) ((num_sub w) 1)))) (λw. λ€. λ{. ((e {) (€ b)) ((w (€ c)) ((num_sub {) 1)))) (λm. (λw. m (w w)) (λw. m (w w)))) ((a f) ((a f) ((a f) ((a f) ((a f) ((a f) ((a f) ((a f) ((a f) i)))))))))) 99) 79) 88) 45) (λw. (cmp_eq w) d)) ((num_sub 1) 1)) (λm. λw. w)) (λw. λ€. w)) (λ{. λm. λw. (w {) m)
Input:
---
---
---
Input:
---
---
---
Input:
---
---
---
Input:
---
---
---
turnstyle.exe: primitive cmp_eq bad argument #1: expected number but got function application
HasCallStack backtrace:
  collectBacktraces, called at libraries\ghc-internal\src\GHC\Internal\Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
  toExceptionWithBacktrace, called at libraries\ghc-internal\src\GHC\Internal\IO.hs:260:11 in ghc-internal:GHC.Internal.IO
  throwIO, called at hs/lib\Turnstyle\Eval.hs:60:17 in turnstyle-0.1.0.0-baa11e7fe9653fbd9855b18808eaca572ad8d900:Turnstyle.Eval

With the 0, 1, 2, 3 being my input and the expression + output only showing up when the program stops due to the crash. Is this something that could be changed?

(Also i guess its tough to improve the error messages, right? Ideally it would tell me in which cmp_eq call the issue is, like which LET expression for example)

jaspervdj commented 1 month ago

With the 0, 1, 2, 3 being my input and the expression + output only showing up when the program stops due to the crash. Is this something that could be changed?

This is not intentional. It worked with line buffering for me (like on the website). I've explicitly set it to use line buffering in 3e4e783, maybe that helps?

(Also i guess its tough to improve the error messages, right? Ideally it would tell me in which cmp_eq call the issue is, like which LET expression for example)

I'd like to work on a step debugger a bit, but you would be seeing the evaluation steps of the image, not the source code. So no LET and colors instead of variable names. So I'm not sure how useful it would be. Maybe it's better to add this to the website version. What do you think?

JanEricNitschke commented 1 month ago

That helped! Now i am also trying to debug my code in the playground, but its pretty hard because i cant actually see the cursor because the image is so large. Not sure what a solution to that be.

I think evaluation steps in the image would already be very helpful. With a bit a of practice and a couple of runs one could probably identify which part of the image corresponds to which structure of the expression (probably defined in a LET)

JanEricNitschke commented 1 month ago

Finally got it to work: https://github.com/JanEricNitschke/TicTacToe/actions/runs/11132154587/job/30935353317?pr=85#step:6:1

Just had a bunch of stupid mistakes in there.

jaspervdj commented 1 month ago

Very impressive! I'll likely use this program as a future benchmark for the compiler, since I don't have any examples this big.

JanEricNitschke commented 1 month ago

Thanks, definitely, go ahead. The normal version was pretty fast. With the -O option i think it took somewhere between 3 and 5 hours on my machine.