10 ms·
Klisp – An implementation of the Kernel programming language
- mchahn 11y ago> Try that in your programming language ;) Does this count? function and(a,b) { return (a ? b ? b : false : false) }
- cosarara97 11y agoNo, because when calling your and(a, b) you will evaluate both a and b, while I expect the one on that page to short-circuit. PS: Also, I'm not sure because I don't know Kernel, but it could be that the function of the page can take any number equal or greater than 2 arguments (and a b c).
- ycmbntrthrwaway 11y ago> it could be that the function of the page can take any number equal or greater than 2 arguments (and a b c) It can. That is what line 5 is for. If eval returns true, $and? is applied to the rest of the arguments, aka cdr.
- qwertyuiop924 11y agoNow try to run this: and(false, console.log("This shouldn't run")) A critical part of the semantics of the kernel function, and the javascript built in &&, is that they are _short circuit_, that is, they only evaluate the second value if the first value evaluates to false. This is very important if you're programming with side effects.
- Sanddancer 11y agoHis function will short circuit. It'll expand to: false ? console.log("This shouldn't run") ? console.log("This shouldn't run") : false : false The ? operator checks the left hand side, branching on whether it's true or false. So it'll only execute b if a is true. Otherwise, it jumps to the false section, which returns false, short circuiting. However, there is a problem with that code in that it'll run the b command twice if they're both true. So and(a,b)(a?(b?(true):false):false) will both short circuit, and will only evaluate each variable once. It gets to the first test, a?, and checks a's value. a is not true, so it jumps to the false section, which returns false, never executing b. Another way of rewriting it would be if (a) { if (b) { return true } return false } return false
- dsp1234 11y agoHis function will short circuit. Not in JavaScript. This is prevented by 11.2.3(3) which specifies that a function call evaluates it's arguments, and which occurs before function evaluation. Note that the actual call to the function to have it's code run doesn't occur until 11.2.3(8), so by then the full arguments list has already been evaluated. Since this is part of the specification, it is not possible for a JIT to completely elide the argument evaluation unless it can prove that there are no side effects (which may or may not be something that individual engines do). "Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4)." "Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values." This is also super easy to test: > and(false, console.log("This shouldn't run")) This shouldn't run false
- Sanddancer 11y agoOkay. I was testing it in C# where it does short circuit, and functions aren't called until the conditions require them to be called. This is also how the Kernel language seems to operate. I have...misgivings about javascript's pre-evaluation of functions.
- cyphar 11y agoC does the same thing. I didn't know C# followed the functional train all the way to lazy evaluation of arguments. But given the fact that C# isn't purely functional, that seems like a bad design decision.
- tyoverby 11y agoC# does not have lazy argument evaluation. Arguments are fully evaluated for each call.
- cyphar 11y agoYeah, I thought as much. Haskell and (some?) LISPs are the only languages I can think that actually advertise this (but there's probably others). C# isn't purely functional, so lazy argument evaluation would be a very wonky feature.
- wtbob 11y agoThis is from the SBCL source, and I think it's pretty elegant: (defmacro and (&rest forms) (cond ((endp forms) t) ((endp (rest forms)) ;; Preserve non-toplevelness of the form! `(the t ,(first forms))) (t `(if ,(first forms) (and ,@(rest forms)) nil))))
- mst 11y agoRight, what's interesting about klisp is that you can define that as something that can be called at runtime, rather than having to be implemented as compile-time macro expansion. I sort of regard operative lisps as "lisp, but even more so", as it were (and for sheer brain melting power when prototyping weird logic they're even more fun than normal lisps, at least to me)
- qwertyuiop924 11y agoI just like them because fexprs are in some ways more elegant and easier to understand than macros.
- karavelov 11y agoWhy not just: function and(a,b) { return (a ? b : false) }
- qwertyuiop924 11y agoStill won't work. See comment above for why.
- karavelov 11y agoYes, I posted before seeing the other comments, the problem is that you can't force lazy evaluation of the function arguments in javascript. On the other hands it can be as easily written in languages that support it, for example in Scala: def and(a: Boolean, b: =>Boolean) = if (a) b else false And with Scala it can be easily made to work as an infix operator: implicit class AndOp(a: Boolean) { def and(b: =>Boolean) = if (a) b else false } Verification: def foo = { println("foo"); false } scala> false and foo res2: Boolean = false scala> true and foo foo res3: Boolean = false
- qwertyuiop924 11y agoHey, neat. Now can you rewrite the code you call and with, inside of and? I kid.
- qwertyuiop924 11y agoKernel is what I really want scheme to be. It's like scheme, but better. And if you're wondering why I say better, more first-class objects is always a good thing.
- shasta 11y agoThat's a pretty simplistic explanation. There are lots of ways to mess up Lisp in an attempt to enable the style of meta-programming that Kernel supports. What distinguishes Kernel from these other approaches (many of which have been seriously considered in the past) is mostly what you're not able to do in Kernel. That is, the vau construct still allows encapsulation in a useful way. Kernel may have "more things first class" than Lisp, but that's not all that sets it apart.
- deleted 11y ago[deleted]
- qwertyuiop924 11y agoThat's certainly one way of looking at it, and it IS one of the reasons I like kernel. fexprs > macros because of this.
- sklogic 11y ago> more first-class objects is always a good thing Only if they're not enforcing some (actually, the worst possible) evaluation strategy.
- qwertyuiop924 11y agoTrue, I suppose.
- groovy2shoes 11y agoWhen you get first-class fexprs, you pay for it in performance. They're nearly impossible to compile, and even interpreting them efficiently is a challenge.
- corruptio 11y ago#define and && heh
- cmrdporcupine 11y agoYep, had the same thought. #define AND(a,b) a && b
- analognoise 11y agoSee answer above about arbitrary number of arguments.
- cpeterso 11y agoiso646.h already `#defines and &&` for you: https://en.wikipedia.org/wiki/C_alternative_tokens https://en.wikipedia.org/wiki/C_alternative_tokens
- def- 11y ago> Try that in your programming language ;) Short circuiting in Nim, seems simple enough: template myAnd(a, b: bool): bool = if a: b else: false
- boken 11y agoThis doesn't quite hit the mark. The example given in Klisp will work with an arbitrary number of arguments. What looks to be the second of two named arguments, e, is actually the dynamic environment from which $and? is called.
- kazinator 11y ago> e, is actually the dynamic environment from which $and? is called. I.e. this and operator is an anti-deluvial beast once known in the Lisp word as a FEXPR. If a compiler is developed, every such operator will have to be reimplemented as a macro.
- mroll 11y agobut not a macro in the sense Common Lisp knows them... FEXPRs are first class objects. FEXPRs can be passed around as values of variables, whereas macros cannot
- kazinator 11y agoFEXPRs operate on a second-class representations of code at run-time: raw source code made of nested lists, which are selectively evaled. Speaking of which, eval is also a first-class function; that doesn't mean it's a good idea to use all the time.
- mroll 11y agoThe point isn't that eval or fexprs are good to use all over the place. It's interesting as an exercise in language design to see what is possible when they are available. And to the fact that fexprs operate on second class data. It's still a win that they are first class objects. It means you can dynamically pick which fexpr (or applicative operator) to call on a set of arguments, which like you said, can be selectively eval'd.
- S4M 11y agoCan't build it on Debian, getting: /usr/include/features.h:374:25: fatal error: sys/cdefs.h: No such file or directory # include <sys/cdefs.h> (I installed uuidcdef, for what it's worth). Also I don't understand the code for the operator and, isn't it gonna return #t whenever x is null?
- striking 11y agoBuilds just fine on Arch Linux. Maybe this article might help you: http://askubuntu.com/questions/470796/fatal-error-sys-cdefs-h-no-such-file-or-directory/ http://askubuntu.com/questions/470796/fatal-error-sys-cdefs-...
- S4M 11y agoYep, it works, thanks for pointing it!
- gjm11 11y agoYes, it is. But x isn't one of the operands -- it's a list of all the operands. And (and) should evaluate to #t for the same reason as (+) should evaluate to 0 and (*) should evaluate to 1. (And also why 0! = 1.)
- deleted 11y ago[deleted]
- prestonbriggs 11y agoOne of Shutt's advisors (on his thesis) was Shriram Krishnamurthi who certainly understands Scheme deeply. Makes me curious to read the thesis in an effort to understand what's new and interesting here.
- Someone 11y agoIn Swift: func myAnd(lhs: Bool, rhs: @autoclosure () -> Bool) -> Bool { return lhs ? rhs() : false } (Heavily inspired by https://medium.com/swift-programming/facets-of-swift-part-5-custom-operators-1080bc78ccc#.j2kertet1 https://medium.com/swift-programming/facets-of-swift-part-5-.... Not tested; any bugs likely are mine)
- junke 11y agoI don't know Swift, but it seems[0] that it is not possible to apply this with multiple arguments, like in the original example (e.g. (and a b c d)). [0] https://stackoverflow.com/questions/29750244/variadic-autoclosures-in-swift-1-2 https://stackoverflow.com/questions/29750244/variadic-autocl...
- namelezz 11y ago> Kernel is a Scheme-like programming language by John N. Shutt in which all objects are first-class. Ok, but what problems are this programming language trying to address?
- joelg 11y agoWhat problems were a Turing machine designed to "address"? Sometimes the things that have the most value are the things that we can't wrap our minds around yet. Think forward.
- groovy2shoes 11y agoThe Entscheidungsproblem: https://en.wikipedia.org/wiki/Entscheidungsproblem https://en.wikipedia.org/wiki/Entscheidungsproblem
- prestonbriggs 11y agoLooking at Shutt's thesis (I've just started), he makes the point that Scheme is pretty 1st class, but not entirely: It has a few reserved forms, e.g., if, lambda, quote. This never particularly bothered me, but errhaps this is what Shutt is attacking.
- sdegutis 11y agohttp://clojure.org/ http://clojure.org/ used to have a macro definition of either (and) or (or) somewhere on one of its basic pages. They reorganized it recently though so I can't find it. But yeah, I'm not really sure what Klisp offers over Clojure so far. This sensationalist version of a hello-world isn't really anything new or special so far.
- PuercoPop 11y agoYou cannot pass macros as values as they do not exist at runtime. (So no macros can be passed to map/reduce as opposed to the fexprs). P.D. Msimoni has written about fexprs (and a Lisp dialect with fexprs on JS, wat.js) if you want to read about them some more.
- sdegutis 11y agoLong ago, when I was learning Clojure, and thought "it's so inconsistent that I can't pass `if` to a function," I would have found fexprs interesting. But I've since thought about it more and realized there's absolutely no need for it at all.
- gliese1337 11y agoCool! I read Shutt's thesis on Kernel years ago, but I didn't know there was an implementation available yet. Vau interpreters can actually be simpler than lambda interpreters, and make a great playground for language design. I wrote a blog post back in 2012 about converting a simple Python Lisp interpreter into a vau interpreter (http://gliese1337.blogspot.com/2012/04/schrodingers-equation-of-software.html http://gliese1337.blogspot.com/2012/04/schrodingers-equation...), and that eventually grew into an undergraduate capstone paper on designing the denotational semantics of and implementing another vau-based language called Vernal (https://github.com/gliese1337/CS598R https://github.com/gliese1337/CS598R). I don't expect any of these will ever become mainstream industry languages, but they have a theoretical elegance and simplicity that makes them fun to play with for the theoretically-minded.
- mst 11y agoThere's also wat.js and my wat-pl port to perl5. They're vastly fun for prototyping complicated logic in.
- klibertp 11y agoIO: myand := method( if(call evalArgAt(0), call evalArgAt(1), false) ) myand(true, 3 println) # prints 3 to stdout myand(false, 3 println) # nothing happens This should also be possible in TCL. Scala has some kind of lazily eval'ed arguments, too. And, of course, Haskell has this built-in. EDIT: better example code.
- klibertp 11y ago(forgot to mention, this IO code generalizes to any number of arguments)
- sklogic 11y agoFexprs do not offer that much vs. mere static compile-time macros, but they're making compilation impossible, limiting an implementation to a lowly and buggy interpreter.