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

Nice mixed pre/infix concept #1

Open
paschlie opened this issue Apr 1, 2024 · 36 comments
Open

Nice mixed pre/infix concept #1

paschlie opened this issue Apr 1, 2024 · 36 comments
Assignees

Comments

@paschlie
Copy link

paschlie commented Apr 1, 2024

However I can't help but wonder if using a different syntactic brace "{}" to explicitly designate an intended pre/in/post-fix expression is truly required or even preferred, vs. identifying such an expressions from its context, i.e. possibly like:

<expression-type> ::
<literal-type>|<object-type>
(<function-type> <expression-type>*)
(<prefix-operator-type> <expression-type>)
(<expression-type> <infix-operator-type> <expression-type>)
(<expression-type> <postfix-operator-type>)

where an <object-type> or <function-type> must be defined via (def ...),
but whose value be modified via an assignment using (set! <object> <value>) or (<object> := <value>)

(Where it's assumed some sort of type analysis/conversion is performed with some order of precedence to enable a logical pairing between the <???-operator-type> and its operand(s) <expression-type>)

And for what it's worth, I'd tend to prefer use of a historical assignment operator such as ":=" in lieu of "<-".

Yielding something like:

(if (x<3) (x:=2*x^3) (set! x (x/2)))

@damien-mattei
Copy link
Owner

I'm not sure to understand. But using ( ) instead of { } could lead to confusion between the regular use of ( ) in lisp/scheme and the new use. Why { } is explained in SRFI-105 in the section Design Rationale : https://srfi.schemers.org/srfi-105/srfi-105.html

About := instead of <- , i used := before <- and decided to choose <- which is already used also historically in APL language, which i never learned but i learned PASCAL and it used :=.

Note that in scheme unfortunately i need 2 operators <- for set! and <+ for 'define' which add a variable in the environment.Instead Python use only =. If used again := ,then i should also perheaps use something like :+ to define a variable.

In fact many possibilities are already provided in Scheme+ : <- and <+ but also ← and ⥆ (which are hard to generate on keyboard, for the history IBM had a special keyboard for APL programmers) , and -> and +> ,etc , so i think i can add again back the := operator for assignment, i will try to remember to do it in a next release.

@paschlie
Copy link
Author

paschlie commented Apr 1, 2024 via email

@paschlie
Copy link
Author

paschlie commented Apr 1, 2024 via email

@damien-mattei
Copy link
Owner

I released a new sub-version with the := operator again.

I understand your ideas. I suppose it could be done, it seems to have been studied in discussions in the SRFI 105 , see part : "Why not autodetect infix?". It would require to know/detect the type of expressions. It would be a totally different implementation with no use of the already implemented SRFI 105.

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@damien-mattei
Copy link
Owner

hello,
about (a + b) vs {a + b}, i admit it could be done but my work was based on SRFI 105 that already did the job. And my compilation and lexical parsing course are far away behind me in the past. After that, using only ( ) add a functionality more to the ( ) making code less readable with complex expressions.

About if then else , it is a minor feature, i added it at a point when the project is finish (?) .It could be good for many things: As you say it to improve the code reading:

  • impropve code reading
  • be used as begin, like (then ...) (else ...) instead of (begin ...) which are macro i used in the past

And it is implemented in a way it is 100% compatible with the classic scheme 'if'

here examples i commented in source code:
;; > (if #f else 3)
;; 3
;; > (if #t else 3)
;; > (if #t 2 else 3)
;; 2
;; > (if #t then 2 else 3)
;; 2
;; > (if #f then 2 else 3)
;; 3
;; > (if #f then 1 2 else 3 4)
;; 4
;; > (if #t then 1 2 else 3 4)
;; 2
;; > (if #t 1 2 3)
;; 3
;; > (if #t then 1 2 else 3 4 then 5)
;; . . SRFI-105.rkt:181:17: if: then after else near : '(then 5)
;; > (if #t then 1 2 else 3 4 else 5)
;; . . SRFI-105.rkt:181:17: if: 2 else inside near: '(else 5)
;; > (if #t else 1 2 then 3 4)
;; . . SRFI-105.rkt:181:17: if: then after else near : '(then 3 4)
;; > (if #t then 1 2 then 3 4)
;; . . SRFI-105.rkt:181:17: if: 2 then inside near: '(then 3 4)

you can read the code yourself here if you want:
https://github.com/damien-mattei/Scheme-PLUS-for-Racket/blob/main/src/SRFI-105.scm#L122

so it is fool proof again a lot of errors.

I admit that i try to implement it with macros but was too complex so i put it directly in the reader.

anyway normal (if test then-expression else-expression ) still works.

note that:

if (a < b) (else …)) ERROR

is not an error:

(if {2 < 3} else "no error")

it simply return nothing as there is no expression.

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@damien-mattei
Copy link
Owner

strange... i was just thinking to explain better about 'separator which is entirely foreign to lisp-like languages' and i wanted to point that the 'cond' of scheme introduce already an 'else' but this is an exception, i can remember that LISP instead use a #t at the end of a 'cond' instead of 'else'. LISP is more pure than Scheme about that. In fact i do not see what problem is with having some reserved words such as 'then' 'else' 'until' but perheaps it has something to do with lambda calculus?

about this syntax:
(if (a < b)
(then (a := (a + b)) (a * b))
(else (b := (a - b)) (a / b))))
it is true that it would be a pretty language.I like it too, just id did not know how to implement it.
note that it can even be simplified by removing unecessary parenthesis:

(if (a < b)
(then (a := a + b) (a * b))
(else (b := a - b) (a / b))))

this is the way works my Scheme+ because it implement operator precedence you can write directly this:

(if {a < b}
(then {a := a + b} {a * b})
(else {b := a - b} {a / b}))

here is a real test in Scheme+ for Racket:
note that you can use 'then' in scheme but not 'else' as it is already used in 'cond' , in the past i used then-block else-block:
https://github.com/damien-mattei/library-FunctProg/blob/master/if-then-else.scm#L19

Welcome to DrRacket, version 8.12 [cs].
Language: reader "SRFI-105.rkt", with debugging; memory limit: 8192 MB.

(define-syntax then-block
(syntax-rules ()
((_ ev) ev)
((_ ev ...) (begin ev ...))))

(define-syntax else-block
(syntax-rules ()
((_ ev) ev)
((_ ev ...) (begin ev ...))))

(define a 3.5)
(define b -2.7)
(if {a < b}
(then-block {a := a + b} {a * b})
(else-block {b := a - b} {a / b}))

0.564516129032258

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@paschlie
Copy link
Author

paschlie commented Apr 6, 2024 via email

@damien-mattei
Copy link
Owner

yes i have done it. Note that Scheme+ not only does infix but also indexes vectors,strings,arrays,etc with [ ] . All those features requires to work the parser, perheaps not only use macro.

@paschlie
Copy link
Author

paschlie commented Apr 8, 2024 via email

@damien-mattei
Copy link
Owner

about application specific language: "Racket is not Scheme" is not really true, it is scheme compatible but it is used to construct language too, but i have poor knowledge about the racket's feature about creating language. I know they implements for example Algol 60 so i suppose they can implements any language perheaps.

note that evaluating (a * b + c) require to know the type of a to detect it is not a procedure but a number or matrix etc... so this could be done by different ways:
-type declaration , example in Typed Racket but not Scheme
-type inference, example in Haskell, but very hard, i do not know a scheme implementation making this
-runtime, the way scheme works

But detecting at runtime can not be done at the parser stage, the modification are deep in the scheme implementation to make them.
Scheme implementation are generally based on lambda calculus which is a sort of limited rewrite rules system and there is no type consideration in it.

for memory there would be 3 sort of evaluation in such a new scheme:
(procedure arg1 arg2 ....)
(special-form-macro arg1 arg2 ....)
(arg1 operator arg2 .....)

@paschlie
Copy link
Author

paschlie commented Apr 9, 2024 via email

@damien-mattei
Copy link
Owner

but if you have so much idea about the infix notation for scheme with ( ) why do not talking yourself with the Racket team?

@paschlie
Copy link
Author

paschlie commented Apr 9, 2024 via email

@damien-mattei
Copy link
Owner

advice is to use #%app to implement what you are talking.
I will do it just when i get time but not sure it is faisable till the end because i can detect procedure? there is no predicate for special form for the reason that (macro? mac) would cause a bad-syntax immediately... so not easy,and all that #%app is not portable...

@damien-mattei damien-mattei self-assigned this Aug 17, 2024
@damien-mattei
Copy link
Owner

i just commited a version that do it....

{(a * x) + y + (d * x) / (add1 (x ** 2))}

{(ksx / (sqrt 2)) * (x + y)}

(define (σ z̃)
{1 / (1 + (exp (- z̃)))}

(define z 3 * 5 + 2)

(define a 2 * (cos (2 * pi * p / q)) )

@damien-mattei damien-mattei reopened this Nov 28, 2024
@paschlie
Copy link
Author

paschlie commented Nov 30, 2024 via email

@damien-mattei
Copy link
Owner

damien-mattei commented Nov 30, 2024

thank you.
yes it would be more consistent but (define var arg1 arg2 ...) is not existing in scheme, so i can not resist using it for infix as it does not break any compatibility.
(define (σ z̃) {1 / (1 + (exp (- z̃)))}
sorry indentation disappeared, just the definition of sigma function taking z tilde as argument

for your last point it is not so easy, first i de not use only a macro there is:
-the SRFI 105 curly infix reader/parser that i have a bit modified
-macro generally with syntax transformers
-procedures

using just ( ) could be done in Racket specifically but not compatible with Scheme

there is not a way to test in scheme if an object is a procedure.
The algorithm i use is to test if there is an operator in (arg1 op arg2) cause i have a list of valid operator but even this is risky,consider:

(map - ns)

is it infix? or prefix?
{ns := (list 1 2 3 4)}
{result := (map - ns)}

will fail :
{result := (map - ns)}

($nfx$ result := (map - ns))
. . ../../../../../../racket/collects/racket/private/kw.rkt:1260:25: -: contract violation
expected: number?
given: #procedure:map

this will works:

{ns := (list 1 2 3 4)}
{op- := -}
{result := (map op- ns)}
result
(-1 -2 -3 -4)

because op- hide the - operator, so the expression is considered prefix, not infix

but your are right, with a modified parser we can only use parenthesis, i have all the code and stuff to do it now, that could be interesting ......

@damien-mattei
Copy link
Owner

damien-mattei commented Dec 1, 2024

seems almost done, will check tomorrow more examples:

(define-infix (foo) (3 * 5 + 2))

(define-infix (foo) (3 * 5 + 2))

#

foo

foo
#procedure:foo

#

(foo)

(foo)
17

#

(define-infix (foo) (3 * 5 + 2) (2 - 3))

(define-infix (foo) (3 * 5 + 2) (2 - 3))

#

(foo)

(foo)
-1

#

(define-infix (foo) (if (2 > 3) "never" "right"))

(define-infix (foo) (if (2 > 3) "never" "right"))

#

(foo)

(foo)
"right"

#

still working prefix:

(define-infix (foo) (if (> 2 3) "never" "right"))

(define-infix (foo) (if (> 2 3) "never" "right"))

#

(foo)

(foo)
"right"

define-infix should be renamed define , like both syntax are accepted....

@damien-mattei
Copy link
Owner

damien-mattei commented Dec 1, 2024

note: this even not require parser! simply macros as you mentioned it month ago, i was too obsessed by SRFI 105... but SRFI 105 is still usefull for indexing arrays [ ]
like this {T[3]}

will check tomorrow for infix again, but if it works, it was just changing one or 2 lines in the code here:

(define-syntax define-infix
	  (syntax-rules ()
	    ;; original define for procedures

	    ((define-infix (name arg ...) body ...)
	     (define-scheme (name arg ...) ($nfx$ body) ...))

just have to add $nfx$ above

thank you for insisting so much , it was worth it.

@paschlie
Copy link
Author

paschlie commented Dec 1, 2024 via email

@paschlie
Copy link
Author

paschlie commented Dec 1, 2024 via email

@paschlie
Copy link
Author

paschlie commented Dec 1, 2024 via email

@damien-mattei
Copy link
Owner

or … (if (: a < b) c (: a := a + b / 3)) absent the ability to differentiate the type of the first term in an expression to classify an expression encapsulated by parens as being prefix vs infix; to my tired (being older than not) eyes, the use of a colon visually accentuates the beginning of an infix-expression better than surrounding it with curry braces and seems more consistent with the syntax of the language in general (but I understand that’s subjective).

On Nov 30, 2024, at 9:37 PM, Paul Schlie @.> wrote: or possibly similarly define a syntax-rule “:" such that “(: <some-infix-expression) using your $nfx$ and assuming all procedure calls remain “( <args)”, then ... (define x (: 3 + 6 / 3 * (radom 3))) or (define (x b) (: 3 + b / 3 * (random 3))) > On Nov 30, 2024, at 7:06 PM, Damien MATTEI @.> wrote: > > > seems almost done, will check tomorrow more examples: > > (define-infix (foo) (3 * 5 + 2)) > > (define-infix (foo) (3 * 5 + 2)) > > # > > foo > > foo > #procedure:foo > > # > > (foo) > > (foo) > 17 > > # > > (define-infix (foo) (3 * 5 + 2) (2 - 3)) > > (define-infix (foo) (3 * 5 + 2) (2 - 3)) > > # > > (foo) > > (foo) > -1 > > # > > (define-infix (foo) (if (2 > 3) "never" "right")) > > (define-infix (foo) (if (2 > 3) "never" "right")) > > # > > (foo) > > (foo) > "right" > > # > > — > Reply to this email directly, view it on GitHub <#1 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOQAXGVTJ3YO6B6U3XEMOZT2DJHGRAVCNFSM6AAAAABSV6GYP6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMBZGQ4DGNZQG4. > You are receiving this because you authored the thread. >

i want a mathematical syntax, having a (: before infix is not mathematical

@damien-mattei
Copy link
Owner

this is subjective, of course ,i can think of the possibility to provide many solutions if they are not confictual in the implementation, already i think that : is the same as $nfx$ that i can export to user toplevel.

another think i must have a coherent system, currently if define-infix a procedure if there is inner define they will be forced define-infix ,that a problem i must solve. A possibility would be for coder to use define and inner define-infix when needed but the inner block will not be infix so , unless i use some portion with { var := infix expression} ,already for this reason it is interesting to have curly bracketts . Ok it works, but the simpliest it will be the more elegant it would be. About if then else i already have in scheme+ $> that is begin (it is short and cause less indentation, $+> is a (let () that allow define inside, due to limitation in some scheme implementation.

@paschlie
Copy link
Author

paschlie commented Dec 1, 2024 via email

@paschlie
Copy link
Author

paschlie commented Dec 1, 2024 via email

@damien-mattei
Copy link
Owner

damien-mattei commented Dec 2, 2024

i agree with you, i expect a 100% backward compatibility or almost, we can not change the habit of users, having something we do not really when we can use infix or prefix would be bad.
It must be proposed a syntax where 'define' is always the way it used to works with prefix. For 'define-infix' i have the idea to detect nested 'define' and escape from infix mode to fallback to prefix one. This should be faisable.

Note that all this is possible with syntax transformers, not the way macro used to works with define-macro but with define-syntax and R6RS,R7RS. When i started the project i did not knew that so i was sticked to SRFI 105 and parser. Things has evolves since.

the notation should be able to write: (if (a < b) {a := b / (random 10)} 0)
in a 'define-infix' but if inside there is an inner definition with 'define' , one should be able to write


(define-infix (foo a b)

      (if (a < b) {a := b / (random 10)} 0)

      (define ns (list 1 2 3))

      (define result (map - (cons a ns)))

      result)

the crucial part being here (map - ....) being not infix but prefix.

As the macros are basically recursive in a define-infix it will modify all the define arguments considering them infix, that i must change by detecting inner define in define-infix.

I have to perform tests about all that, because the system is becaming more and more complex, syntax transforming is done only one time ,so there is by chance no time penality in a looping program or recalling the same definitions.

But the more the system is complex ,this could lead to bugs.

Also i have 'def' that act as python allowing 'return' , i would have again a 'def' and a 'def-infix' , even if the 2 form are proposed, the best programming style would be to use the infix ones and only when necessary use inner define prefix ,they are very rare ,all that is just to do (map - ns) and sort of things that are 1% of programming but we can not leave that wrong or impossible....

note : it is hard to write code, but you can re-edit your own message by clkiking at right on ... edit

@paschlie
Copy link
Author

paschlie commented Dec 2, 2024 via email

@paschlie
Copy link
Author

paschlie commented Dec 2, 2024 via email

@damien-mattei
Copy link
Owner

yes my presentation is not good.

historically there is:
-SRFI 105 curly infix for infix and array indexing too
-but as infix precedence was not deal, i made it (this required macro, with syntax transformer that help a lot,there is too much problem without, eval problem and, or are macro not procedure so you can not deal with them in normal macro, need syntax transformer, operator overload is solved too by syntax transformers)
-then the idea of ( ) , done by a recursive macro and procedure at the syntax transforming phase but the appear the (map - ns) problem, only it really is a problem. This ( ) could be used inside { } but not only see below... (note at this phase we can only do almost anything we want in both prefix and infix)
(define var arg1 op1 arg2 op2 arg3 op3 ...) is a special case ,do not take it in acccount)
-then define-infix is introduce to use ( ) outside any { } but this require a define-infix to recursive parse all the inner ( ) and then the (map - ns) problem arise again.
So the solution is to recurse and check the occurence of (define something the old scheme way) ,find (define ,deactivate the infix parsing of ( ) in something the old way and reactivate it when we find the closing ) . I already did such a thing in the SRFI parser recently for quoted expression symbolic where i did not want to happen an $nfx$ that would be never evaluated as it is a quoted expression. Not too hard but as the code is becoming complex and i have not a lot of time now, i will do it indeed.

@damien-mattei
Copy link
Owner

damien-mattei commented Dec 5, 2024

it is done (but not commited) , just a few lines to add at the good place:

 ;; if there is (define ... we must not compute deep-terms with recall-infix-parser but simply copy the terms in deep-terms
  (define deep-terms terms)

  (when (not (datum=? 'define
		      (car terms)))
    (set! deep-terms (map (lambda (x)
			    (recall-infix-parser x operator-precedence creator)) #;recall-infix-parser
			  terms)))
  

this allow define to be the usual way, the rest infix. But it is an endless story , toplevel statements not in a define should be parsed to , this will be done by a modified (module ... macro .

not yet all tested but it shapes well.

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

2 participants