Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wonderful! #219

Open
Geordi7 opened this issue Apr 11, 2021 · 0 comments
Open

Wonderful! #219

Geordi7 opened this issue Apr 11, 2021 · 0 comments

Comments

@Geordi7
Copy link

Geordi7 commented Apr 11, 2021

I commend you on making a beautiful little language. I have long suspected that what you call "concatenative" style is where PLs should be heading, and you've gone and done it!

Thoughts:

1: Did you really mix {} enclosed scope and whitespace sensitive scope following : ?

2: In a concatenative language, every enclosed expression is also a function:

define play_game (-> +IO +Exit):
    do (while) { play_round maybe_play_again }

{play_round maybe_play_again} has the type (-> bool), and this function could be written:

{do (while) {play_round maybe_play_again}}
(-> +IO +Exit) -> play_game

3: In a concatenative language, type expressions (like (-> +IO +Exit) above) are just another function which the compiler can decide whether to resolve at compile-time or run-time

You propose this syntax for matching:

define read_x_y (Char, Char -> Optional<Move>):
    match (dup2 try_read_x_y)
    case some: \drop2 dip some
    case none: swap try_read_x_y

But this dilutes your syntax IMO, it could look like this:

define read_x_y (Char, Char -> Optional<Move>):
    dup2 try_read_x_y
    (some) {\drop2 dip some}
    (none) {swap try_read_x_y}

Here we are applying a strict rule that parens () denote type constraints, and braces {} denote scopes/control flow. When the compiler encounters a type constraint, it decides whether it can be resolved at compile time (this is valid or invalid code), or at run time (some kind of branch is taking place).

If statements are also just run-time dependent-type constraints, and similarly, loops are constraints that get reevaluated at the end of the activated scope:

define parse_move (List<Char> -> Optional<Move>):
    -> chars;
    do (with (+Fail)):
        if (chars length = 2):
            (chars 0 get from_some)
            (chars 1 get from_some)
            read_x_y
        elif (chars length = 1 && { chars 0 get from_some = 'q' }):
            forfeit some
        else:
            none

could look like this:

define parse_move (List<Char> -> Optional<Move>):
    -> chars;
    do (with (+Fail)) {
        chars length
        (= 2) {
            chars 0 get from_some;
            chars 1 get from_some;
            read_x_y
        } (= 1) {
            chars 0 get from_some;
            (= 'q') {forfeit some} {none}
        } {none}
    }

The implication here is that

(constraint1) {scope1} (constraint2) {scope2} {scope3} behaves like

if (constraint 1) {scope1} else if (constraint2) {scope2} else {scope3}

I don't know if you like this... but I do :D

Of course, if you follow this approach, you will run into problems scoping other expression types without the use of () my suggestion would be to exchange () and {} such that parens denote scope, and braces denote constraints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant