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

Two new examples for the lurk-lib repository #32

Open
wants to merge 3 commits into
base: lib
Choose a base branch
from
Open

Conversation

dhsorens
Copy link

@dhsorens dhsorens commented Dec 2, 2022

This PR contains two new examples for the lurk-lib repository.

  1. The first is a gcd function that takes into account the modular arithmetic native to a finite field (it won't break for too-large of numbers).
  2. The second is a recursive function which behaves like a (purely functional) smart contract, which comes with an example counter contract

@dhsorens dhsorens mentioned this pull request Dec 2, 2022
@dhsorens dhsorens marked this pull request as draft December 2, 2022 12:30
@dhsorens dhsorens marked this pull request as ready for review December 2, 2022 12:30
if (= a 0) 0 (
if (= b 0) 0 (
let ((q (quot a b 0)) (r (rmdr a b))) (gcd b r)))))))))
(current-env))
Copy link

@weissjeffm weissjeffm Dec 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't dug into the content yet, but I notice the formatting. Have a look at this to see how lisp is supposed to be formatted:

https://ashok-khanna.medium.com/formatting-lisp-5e28020b8bac

TLDR: should look more like this:

(letrec ((quot (lambda (a b q)
		 (if (< a b) (quot b a 0)
		     (if (> (* b (+ q 1)) a)
			 q
			 (quot a b (+ q 1))))))
	 (rmdr (lambda (a b)
		 (if (< a b)
		     (rmdr b a)
		     (- a (* (quot a b 0) b)))))
	 (gcd (lambda (a b)
		(if (< a 0)
		    (gcd (- 0 a) b)
		    (if (< b 0)
			(gcd a (- 0 b))
			(if (= a 0)
			    b
			    (if (= b 0)
				a
				(let ((q (quot a b 0))
				      (r (rmdr a b)))
				  (gcd b r)))))))))
  (current-env))
  • parens always up against contents or other inner parens
  • format if with line break after condition
  • format lambda with line break after arglist, unless the whole form fits on one line

Copy link
Author

@dhsorens dhsorens Dec 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, yes sorry about that, I pushed old code without properly reviewing it. thank you for spotting

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to mention the reason for all this - you don't generally look at lisp parens. The parens are for the editor and compiler, not the human reader. The editor auto-indents based on the paren structure, and your eyes easily pick up the shape of a let or if and their component parts.

Of course within a single line you do need to use the parens, which is why we tend to avoid deeply nested structure on a single line. Usually 3 or 4 levels or so is where it starts to get ugly and you make it more vertical and get more nice indentation.

The deeply nested if above is not very typical. What you'd normally see in other lisps is cond, which does the same thing without the nesting:

(cond
  (< a 0) (gcd (- 0 a) b)
  (< b 0) (gcd a (- 0 b))
  (= a 0) b
  (= b 0) a
  T (let ((q (quot a b 0))
	  (r (rmdr a b)))
      (gcd b r)))

I would expect eventually we'll have cond in lurk.

Copy link
Author

@dhsorens dhsorens Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great insight, thank you for sharing. cond would be very useful to keep programs legible for sure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be implemented as a macro. But it's a good observation and it occurs to me that it might be cheap enough, due to how the circuit works, to implement it directly. If we can do so in a way that makes the if case no more expensive than if itself, we could potentially share most/all logic with if.

For example, if instead of a terminal T case, we instead specify that a final, unmatched value is the default failure case, then (if condition true-case false-case) is actually equivalent to

(cond
  condition true-case
  false-case)

I think.

If we were okay with simply generalizing IF in that way, then I think the cost might be minimal (depending on some details I can't think through at the moment).


I'll think about it. But regardless, we can definitely implement COND as a macro in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One note, though: the example code @weissjeffm wrote above is not actually structured the way Lisp (Common Lisp and Scheme's) COND is. That's handy for the idea of a 'non-binary IF', but not so much for idiomatic Lisp. The added structure of 'real' COND is actually pretty valuable.

That said, I guess @weissjeffm's COND is the same as Clojure's…

I'd argue that if we implement COND as a macro, we should follow CL/Scheme. But I could possibly convince myself that a franken-IF would not be horrible.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha yep I wrote clojure cond syntax (more or less, I think it allows an implicit default, I can't remember off the top of my head but I didn't use it just for clarity).

I'm curious what added value you meant from the added structure? I thought of the reduction in paren count as basically free, and so much easier on the eyeballs.

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

Successfully merging this pull request may close these issues.

3 participants