h3rald / min

A small but practical concatenative programming language and shell
https://min-lang.org
MIT License
310 stars 23 forks source link

Lambda Lifters #123

Closed ghost closed 3 years ago

ghost commented 3 years ago

I did a lifter symbol named test:

( symbol test
    (==> 'sym :rsym)
    (
        (* +) ^rsym
    )
) ::

1 2 3 test puts

But that gave the error below:

(!) yolo.min(13,19) [^rsym]: Invalid value for output symbol 'rsym'. Expected symbol, found null
    yolo.min(18,10) in symbol: test
h3rald commented 3 years ago

So if I understand correctly you'd like to be able to push... essentially an unquoted quotation on the stack.

A few problems with that:

ghost commented 3 years ago

So if I understand correctly you'd like to be able to push... essentially an unquoted quotation on the stack.

A few problems with that:

  • at the moment there is no way to express that with the current type system 😬
  • you would need essentially to be able to specify sym as a type (which doesn't exist yet), but it may only work for output values, because input values are already pushed in the stack and it would be too late 😉
  • erhm... there's no equivalent for bind for lambda (essentially it is the same as the pre-0.31.0 define but only works on quotations) -- hopefully it shouldn't be too much of a problem in this case though.

Okay, but how can i solve the error? Do you have any real solution for above example?

Rewrote here:

( symbol test
    (==> 'sym :rsym)
    (
        (* +) ^rsym
    )
) ::

1 2 3 test puts

If we couldn't do that, we'll understand somethings seem not going right. Because lifting, lifting, lifting...

h3rald commented 3 years ago

No, at the moment there is no proper way to implement that, because there's no lambda-bind symbol and because you can't declare sym as a type (but you could use a).

Erhm... there is actually a way to make your code work right now, but it is a horrible, horrible hack:

(symbol test
  (==>)
  (
    (* +) ^test
  )
) ::

Basically the lambda inside its overwriting the symbol you just created... 😳 could be a little bug 🙄 or maybe a really cool feature 😬

In the end though, you can achieve this by creating a symbol using lambda, but not with an operator.

ghost commented 3 years ago

A more real/true example:

( symbol lifter_quo_typeclass
    ('sym :T ==> str :symstr)
    (
        "
            ( typeclass quo_typeclass
                (quot :q ==> bool :r)
                (q $1 all? @r)
            ) ::
        " (T) => % @symstr
    )
) ::

'typeclass:anynum lifter_quo_typeclass parse ^quo_anynum
'typeclass:char lifter_quo_typeclass parse ^quo_char

This typeclass lifter made another bad error. Please fiz it.

ghost commented 3 years ago

there is actually a way to make your code work right now, but it is a horrible, horrible hack:

Hey! This is beautiful solution! Very great, thanks much!

maybe a really cool feature 😬

Recursive symbols?? (like recursive functions)

ghost commented 3 years ago

Recursive symbols?? (like recursive functions)

However, you should drop the stack-pollution preventing feature in symbol signature bodies or you should add an option to off this feature.

h3rald commented 3 years ago

If you don't want stack pollution control... well, you can use a lambda. And I will probably allow sym as a type at least for outputs -- now the potentially dangerous thing is that saying that a symbol is allowed as an output value wouldn't guarantee that no stack pollution occurs: at present I am checking only that the output symbols that are declared are allowed on the stack at the end of the execution of the body quotation, so if a symbol is allowed to go on the stack that's fine as far as the current stack pollution checks go... BUT when pushed on the stack that symbol will be evaluated so, anything could happen!

h3rald commented 3 years ago

Recursive symbols?? (like recursive functions)

Already got that 😉 Double checked and the following works as expected:

(symbol factorial
  (int :n ==> int :o)
  (
    (n 1 ==)
      (1 @o)
      (n n 1 - factorial * @o)
    if
  )
) ::
ghost commented 3 years ago

Recursive symbols?? (like recursive functions)

Already got that 😉 Double checked and the following works as expected:

(symbol factorial
  (int :n ==> int :o)
  (
    (n 1 ==)
      (1 @o)
      (n n 1 - factorial * @o)
    if
  )
) ::

Recursive table:

3 2 factorial * @o
3 2 1 factorial * @o @o
3 2 1 @o * @o @o

I didn't understand how did your example factorial work. Could you explain it detailed?

ghost commented 3 years ago

I understand now:

3 factorial
<3 2 factorial * @o>
<3 <2 1 factorial * @o> * @o>
<3 <2 <1 @o> * @o> * @o>
<3 <2 1 * @o> * @o>
<3 <2 @o> * @o>
<3 2 * @o>
<6 @o>
6
ghost commented 3 years ago

you can use a lambda.

We can't do generics in lambdas. So lambda isn't exactly equivalent to operator signature.

h3rald commented 3 years ago

you can use a lambda.

We can't do generics in lambdas. So lambda isn't exactly equivalent to operator signature.

You are right of course... alright, I'll allow symbol outputs and add lambda-bind...

ghost commented 3 years ago

you can use a lambda.

We can't do generics in lambdas. So lambda isn't exactly equivalent to operator signature.

You are right of course... alright, I'll allow symbol outputs and add lambda-bind...

Okay 👍 Thanks

h3rald commented 3 years ago

Alright, added lambda-bind (~) and allowed lambdas in signatures (for outputs, for inputs they are impossible to capture) -- so the previous example becomes:

(symbol test
  (==> quot ^rsym)
  (
    (+ *) ~rsym
  )
) ::
2 3 4 test puts ; 20

Note the usage of ^ and ~.

I also had to slightly changed the way output values are validated: before output values were pushed on the stack and then the result validated, now I check the value before it is pushed on the stack (which is probably much better).

ghost commented 3 years ago

Alright, added lambda-bind (~)

Perfect!! 👍

Maybe we can control stack pollution in lambdas via =-= (expect-empty-stack) symbol. Am i true?

h3rald commented 3 years ago

Yes definitely... I added that symbol to add optional checks wherever needed 😊

ghost commented 3 years ago

Do you know how much did min slow from the v0.27.0 ?

Did you be sad 😞 to see the min as being slow because of your implementing of my given ideas? (really sorry for that) If you did please cheer up! 💯 It's so much speeddddy... 🏃 💨 #31

h3rald commented 3 years ago

Nooo I am not sorry for implementing your ideas! Most of them are great 😊👍 the others are typically difficult to implement or hard for me to understand 🤣

Regarding the benchmarks... I never found a decent set of benchmarks to run for min. Do you known any I (or you!) could try to implement?

I tried to run some JSON processing benchmark and it seemed to be very slow, like slower than Ruby. But for certain practical things like file processing (I am thinking of HastySite that I use for the min site and my own) it is very fast.

Eventually I am thinking of implementing a command line switch to disable checks. Oh and then of course "compiled" min should be slightly faster (skips the parsing phase entirely at least).

h3rald commented 3 years ago

Implemented support for lambda-bind in v0.32.0.