Hacker News new | past | comments | ask | show | jobs | submit login
Portacle – A Portable Common Lisp Development Environment (shinmera.github.io)
136 points by AlexeyBrin on Jan 4, 2017 | hide | past | favorite | 58 comments



I tried to install this on OSX (10.12.1), it doesn't seem to handle tar.xz files well, when extracted using the (default) "Archive Utility", it produces a ".cpgz" file, which, when extracted, produces a ".tar.xz" file again.

I ended up extracting it using the "xz" utility

    brew install xz
    xz -d ./mac-portacle.tar.xz
Opening the Portacle.app file didn't work, starting it from the terminal gave me the following:

    dyld: Symbol not found: _iconv
      Referenced from: /usr/lib/libcups.2.dylib
      Expected in: /Users/koen/Downloads/portacle//usr/mac/lib//libiconv.2.dylib
     in /usr/lib/libcups.2.dylib
    /Users/koen/Downloads/portacle//emacs/mac/emacs.sh: line 51: 44458 Abort trap: 6               "$SCRIPT/bin/emacs" --name Portacle -T Portacle -q -l "$ROOT/config/emacs-init.el" "$@"


I had a similar problem and reported here: https://github.com/Shinmera/portacle/issues/22

From the author's response, it sounds like building from source may work. I plan on trying later.


This is cool!

Myself, I'm more of a vim than an emacs animal, so I use the “slimv” plugin, the vim sibling of “slime”. With a little glue of my own making, it lets me start the Lisp machine (SBCL) from inside vim, and then I can macroexpand, disassemble, evaluate, compile... without ever leaving the editor. Practically turns the terminal into a complete IDE.


It seems like a good companion for Practical Common Lisp


It was sooooo about time someone pulled the best of the magic forest together in a nice package. Thank you!


I've gotten pretty comfortable with emacs on account of doing Erlang development for work for the past 5 months. This may be gateway for me to start actually learning/using Lisp (at least for personal development).


I keep hearing about the elegance of Lisps, but as someone who learned functional programming in ML, I can't get past the lack of currying. Coupling that with dynamic typing and I feel that I've lost much of what makes Haskell/ML great: programming by composition with strong compile-time guarantees of correctness. Am I missing something?

Edit: to be clear, I'm referring to a lack of automatic currying.


No problem! Here you go!

  (defmacro defun-curried (name param-list &body body)
    (dolist (p param-list)
      (when (member p '(&optional &key &rest))
	(error "DEFUN-CURRIED does not yet handle &OPTIONAL, &KEY, or &REST")))
    (let ((new-params (mapcar (lambda (p) (list p nil (gensym)))
			      param-list))
	  (prelude nil))
      (when (stringp (car body))
	(setq prelude (list (pop body))))
      (do () ((not (and (listp (car body)) (eq (caar body) 'declare))))
	(push (pop body) prelude))
      `(defun ,name ,(cons '&optional new-params)
	 ,@(reverse prelude)
	 (cond ,@(reverse
		   (maplist (lambda (nps)
			      (let ((ra (gensym)))
				`((not ,(third (car nps)))
				  (lambda (&rest ,ra)
				    (apply #',name ,@(mapcar #'first (reverse (cdr nps)))
					   ,ra)))))
			    (reverse new-params)))
	       (t . ,body)))))
Handling &OPTIONAL, &KEY, and &REST in the original parameter list is left as an exercise for the reader :-)

Examples:

  > (defun-curried foo (x y z) (+ x y z))
  FOO
  > (funcall (foo 1 2) 3)
  6
  > (funcall (funcall (foo 1) 2) 3)
  6
Okay, I will admit, the 'funcall' thing is annoying.

In case anyone is wondering how this works, here's the expansion of FOO:

  > (macroexpand-1 '(defun-curried foo (x y z) (+ x y z)))
  (DEFUN FOO (&OPTIONAL (X () #:G839) (Y () #:G840) (Z () #:G841))
    (COND ((NOT #:G839) (LAMBDA (&REST #:G844) (APPLY #'FOO #:G844)))
          ((NOT #:G840) (LAMBDA (&REST #:G843) (APPLY #'FOO X #:G843)))
          ((NOT #:G841) (LAMBDA (&REST #:G842) (APPLY #'FOO X Y #:G842)))
          (T (+ X Y Z))))


You're right that Haskell has different things that make it great, but you're wrong about the "lost" part of it. Lisp has never tried to be what you're describing outside of the occasional research project or sidecar optimizer.

What Lisp has that very few other languages have is the confusingly named "homoiconicicity." Lisp data and lisp code are, by and large, the same thing. As such, it's quite easy to write compiler extensions ad hoc for every given usecase.

This is different from Haskell's babel of language extensions, in that these extensions tend to be razor thin and custom fit syntactic abstractions that provide an opportunity for code reuse or performancs optimization with less leaking of underlying structure.

I've always called it "meta-syntactic" programming.

Lisp sits off in its own little world, and probably has more akin to Forth and Factor than Haskell.


Yes. The REPL and macros, for instance. Also note that Lisp is a multi-paradigm language, not a functional one, so you can explore more ways to design programs. Embrace Lisp's dynamic nature and many interesting things are possible.

The following quote is about Scheme, not Common Lisp, but I think it illustrates the philosophical difference between dynamic languages and static languages. It's from the home page of an Emacs extension for Scheme programming:

    Or, to be precise, what i consider fun. Geiser is thus my humble
    contribution to the dynamic school of expression, and a reaction
    against what i perceive as a derailment, in modern times, of standard
    Scheme towards the static camp. Because i prefer growing and healing
    to poking at corpses, the continuously running Scheme interpreter
    takes the center of the stage in Geiser.
http://www.nongnu.org/geiser/

Building your entire program without ever having to restart it is a powerful development model, and to have the workflow well integrated with Emacs (the only editor for Lisp as far as I'm concerned) yields a programming experience like no other.


Take a look at this recent article about "Rascal", an attempt to combine Lisp-like macros with Haskell-like types and semantics.

https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haske...

You'll see that there is some serious value in Lisp-style programming that's only now being experimentally adapted to Haskell-style type systems (building on very recent research).

Teaser:

"Simple syntactic transformations are, of course, trivially defined as macros. Haskell do notation is defined as an eleven-line macro in rascal/monad, and GHC’s useful LambdaCase extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy."

"While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like GeneralizedNewtypeDeriving and other generic deriving mechanisms like GHC.Generics, DeriveGeneric, and DeriveAnyClass."

The other major benefit of Lisp is interactivity. Haskell has a good REPL, but Common Lisp is way ahead. For example, you can call a function that incrementally recompiles and reloads your system's modules and dependencies. That is simply a function call; when the function returns, the new code is live. Even further, Common Lisp "exceptions" do not immediately unwind the stack, but gives you enormous power over error handling: the system can stop, let you redefine the erroneous function, and then continue where it left off.

I'd love to have the best of both worlds -- Haskell's static safety and rich type system, and Lisp's metaprogramming and interactivity -- but combining them coherently is a huge challenge, so both languages continue to exist as exemplars of two very different approaches which both have value.


As someone who uses both Common Lisp and OCaml, the thing I miss the most from the latter is a compile-time code generation facility, of which Lisp macros are a prime, and yet unrivalled example (only Julia's macros come suspiciously close).

It's funny that you should mention currying. When I started programming, I didn't know of ML (and wouldn't have access anyway) and the decidedly un-mathematical approach taken by practically all other languages irritated the hell out of me. I found “error: too few arguments” always an illogical answer. A literal implementation of Alonzo's great discovery has a clear advantage here. When every multi-argument function is actually multiple nested lambdas of 1 arg each, the opposite direction isn't hard to construct any more.

So, if you're already comfortable in the ML world, I'd say you're not missing anything vital.


> too few arguments” always an illogical answer

If you write math/algo code, yes. Most other types of code benefit a lot from:

- variable numbers of arguments

- default argument values (to practically make use of the one above)

- named/keyword arguments (usually combined with default arg values in same use, which will make obvious the need for specifying arguments in variable orders)

And of course that if you have these, you really don't want to also have auto-currying because it would make implementation hard and code reading or debugging a nightmare! (especially in a dynamic language)

...which may seem like an abomination for both the "clean code / uncle Bob's disciples" side which prefers methods of less that 3 arguments (but apparently they don't mind having a zillion of them spread in a zillion little classes), and for the "strict and auto-curried functional programming" side.

But for real world code written under real deadlines, usually without the time to RTFM of every lib and api you use, makes life 100x easier for regular programmers which aren't always fast enough to get the right abstraction in the allocated time or have good enough memory to figure out the right order and number of arguments a function takes in all cases!

(imho both Clojure and CL seem to have gotten these things right...)


I hear you and I don't disagree. As far as discoverable / self-explanatory API goes, named arguments are a great help. OCaml gives you those, and optional named args with default values. Which, together with strict & strong typing (plus other FP perks), makes the best of both worlds for me.


> OCaml gives you those

I know. And I learned it up to a point... But for me it unfortunately falls into that uncanny valley between truly elegant languages (Haskell, Scheme) and truly practical languages (Python, Ruby, Go). Neither elegant enough nor practical enough for my use. Probably better for other people's uses.


Common Lisp, at least, isn't about functional elegance. It's mostly an imperative language with some functional constructs for convenience. Indeed, it probably has the best iteration facilities of any moderately popular language.


Yet the definition of Lisp is as elegant as it gets. And it potentially has the best of everything, thanks to real macros.


No, it isn't. There's a difference between McCarthy's "Maxwell's equations of software" and the full-blown Common Lisp standard, which was designed by and aimed at industry.


It's still there, it's perfectly possible to not use all the stuff they piled on top. At least it's useful, semi coherent stuff; as opposed to C++.


What if we had this:

  (defun foo (a0 a1 &curry a2 a3 &optional a4 ... &rest r) ...)
The &curry keyword would specify that if this fun is called with fewer than four args, it returns a function. If called with fewer than two, an error is signaled; two are required. If more than four are passed, they satisfy the optionals.

Thus (foo 1 2 3) would return a (lambda (&curry a3 &optional ...) ...).

The main disadvantage is that currying macros do this statically, purely at the call site's compile time, without cooperation from the callee.

Of course, this proposal can cheerfully coexist with currying macros.

In a Lisp dialect which can express multiple function signatures, things are more general; you can do the above yourself. You can make your three-arg function have zero, one and two-arg cases which curry.

For that matter, we can do it like this:

  (defun foo (a0 a1 &optional (a2 :curry) (a3 :curry) a4 ...)
    ;; handle a2 or a3 being :curry by returning lambda)
I'm well aware of presence-indicating arguments: (a2 default-val a2-present-p). With this simple :curry convention, we could simplify a semi-automatic implementation. It has a notational advantage.


I expect this to be useful, enough so that I planned on adding it to a (non-CL) language implementation. (But it hasn't happened yet, so I can't report from experience.)

BTW, while everybody in this thread seems to think that partial application is all there is to Haskell style currying, it isn't. Consider

    (defun foo (x) (lambda (y) (+ x y)))
If you can't call this as

    (foo 1 2)
to get 3, you still don't really have currying.


We cannot call that foo as (foo) either, so we have nothing. We have to do something in foo to make currying work. If (foo 1) is required to return a unary function, of course we support (foo 1 2) which passes 2 to that function.

Here is the problem case:

  (defun foo (x)
    (unknown x))
Suppose unknown can return a function of any number of arguments. The developer of foo doesn't know how many. If it returns a five-argument function, you expect the call (foo 1 2 3 4 5 6) to work.

Basically, we can make foo n-ary such that it calls unknown with the first arg, and passes the rest to the function that unknown returns.

A subtlety not mentioned in the thread is that (x) and x are distinct in Lisps. Applying an empty argument list is different from not applying arguments.


> I feel that I've lost much of what makes Haskell/ML great: programming by composition with strong compile-time guarantees of correctness.

That's true. Common Lisp and Haskell/Ocaml are completely different languages and require very different programming.

Interestingly there is some overlap in applications. ML (actually SML and OCAML) is widely used for theorem provers. Common Lisp has a dozen theorem provers, too. Like ACL2, PVS, OBJ, ...


Common Lisp isn't a language particularly focused on FP, in fact, since pretty much everything is mutable, eager and dynamic, you might consider it downright awful at that. CL has more in common with Smalltalk than with Haskell or ML IMO. It's strength comes from a different way of looking at things: macros, a powerful live environment that's unmatched by pretty much anything, an absurdly stable spec you can rely on, one of the best approaches to OO I've encountered and an scary attention to details in certain parts of the language.

Because it's so flexible and multi-paradigm, you can pretend it's an FP language and maybe get away with it, but you'll have to bring your own bondage and discipline rather than rely on the compiler, or write a language on top of it that does that(check out shen if you haven't).


So what paradigm would you suggest? A combination of imperative with functional where it's better suited? Or is Lisp best programmed with an entirely new approach?


Lisp is a family of languages. CL doesn't define all Lisps. The two big families tend to be Common Lisp and Scheme.

* Clojure - Immutable, functional, has pattern matching.

* Common Lisp - Mostly just multiparadigm, with a leaning towards Object Oriented.

* Scheme - More functional than Common Lisp, less Object-Oriented. (Though Guile tends to have an OO leaning, through its extensions.)

* Racket - Has a mix of lazy, static typed and other sublanguages.

* Picolisp also deserves an honorable mention. It is somewhere between Common Lisp and Scheme, with some clever language features of its own. It's mostly functional, with decent C/Java FFI and a Datalog-like library for OO.


Depends on your problem, experience level, and the context of your overall architecture, team or a million other factors. Apply thought liberally :)


Yes you are: s-expressions, i.e. code and data are represented the same way, which makes it easier for programs to manipulate code. A language could be created that combines the type system of Haskell with the syntax of Lisp. You would trade away a bit of the human readability that the Haskell/ML syntax gives you.

Also, as you have probably seen in other discussions, some people believe dynamic typing gives them more flexibility and power, and they find the Haskell type system frustrating. My perception is that it takes a lot of discipline and deep understanding to make Haskell work, and that's not for everyone, but I won't argue that it's not worth the trouble. I think you will always find some people who prefer discipline and others who prefer flexibility. Each approach has its advantages and difficulties.


> Am I missing something?

Yes: the common sense to not troll Lisp threads. How annoying would it be if for every Haskell thread someone jumped in with a comment like "Haskell is not homoiconic or reflective, I feel I've lost the greatness."? What does your comment even have to do with a thread about a Common Lisp IDE? How much effort would it have been to Google "Lisp currying" or "Lisp static typing"?


Lack of automatic currying provides benefits like:

- optional arguments with default values

- variadic arguments: you can have a single function zip, rather than zip, zip2, zip3, ...

- if you pass too few parameters to a function, it is nicely pinpointed as an error right there rather than producing a functional value.

Manual currying can be reasonably slick with a few macros, like TXR Lisp's op, ap and ret, plus various combinators. Thanks to the unambiguous syntax, it remains readable.

Manual currying can be more fully featured compared to automatic. You can insert the arguments anywhere in any order. Implicit currying only handles the rightmost args.


Take a look at Shen [1]. It's a Lisp with optional typing, optional lazy evaluation, built-in Prolog, pattern matching and more.

[1] shenlanguage.org


One thing that is usually lost to FOSS developers is the tooling of Commercial Common Lisp environments, the genesis of the IDE (alongside Smalltalk and Mesa/Cedar) concept.

Things like Swift Playgrounds and Jupyter notebooks were already part of Symbolics Genera.


Welp, Lisp is not for everyone. If you can't get past the lack of currying, stick with what you've got.



My cheapo one:

(define (curry f . args) (lambda (. cargs) (apply f (append args cargs))))


Looking through the other comments, I now see they were looking for automated currying. I was confused, as currying is trivial—as per your example—and also built in as a named function in Racket.


I'm not sure I understand what you mean when you say there's no currying in common lisp? If you write functions that are curried then you'll have currying. Am I missing something?


Sorry, currying by default. In the ML-inspired languages, unless a function takes a tuple of its arguments it's curried. This doesn't seem to be the case in any of the Lisps I've looked at. You can certainly curry the functions yourself but the language doesn't seem to make that easy.


I'd count ubiquitous currying a design mistake in a dynamically typed language. All your "wrong number of arguments" errors get caught late.

(But the real problem with common lisp is that its version of (+ 2) is the ridiculously verbose (LAMBDA (x) (+ 2 x)) — far too many anonymous functions end up with more boilerplate than content. A decent reader macro might go a long way towards curing curry envy. If I remember correctly, it's [+ 2 _] in Paul Graham's Arc, not bad.)


Using reader macros for syntax is mostly a mistake - with exceptions. The Lisp tradition generally likes to use reader syntax on the level of s-expressions.

If verbose expressions are a problem, normal macros or functions would be sufficient.

    CL-USER 10 > (defmacro rlambda (fn &rest forms)
                  (let ((arg-sym (gensym "ARG-")))
                    `(lambda (,arg-sym)
                       (,fn ,@forms ,arg-sym))))
    RLAMBDA

    CL-USER 11 > (macroexpand-1 `(rlambda + 2))
    (LAMBDA (#:ARG-804) (+ 2 #:ARG-804))
    T
The 'real problem' is that many people have difficulties using and accepting a programmable programming language.


Your "solution" is still more boilerplate than content and befuddles people used to lisp by evaluating in the function namespace an argument in non-function position.

Common Lisp simply can't approach the succinctness and elegance of currying without adding some syntax.

Your resistance is puzzling, btw. You're not having difficulties using and accepting a programmable programming language, are you?


> used to lisp by evaluating in the function namespace an argument in non-function position.

True, macros can confuse people not used to the concept.

For example the DEFUN macro: (defun plus (a b) (+ a b))

'plus' is in the function namespace, even though it is in a non-function position.

Strange, isn't it?

> Common Lisp simply can't approach the succinctness and elegance of currying without adding some syntax.

That's true. But it doesn't even try, because the 'succinctness and elegance' of currying without adding some syntax is a non-goal for Common Lisp. Lisp has always preferred one expression for each function call and over the years it added complex argument lists (variable number of arguments, optional arguments, keyword arguments, ...).

Personally I don't miss things like 'automatic' currying, since they are making the code harder to read (-> currying is not explicitly visible in the source code) and debug.


In Scheme there is the "cut" macro:

    (cut + 2 <>)
GNU Guile specifically has a curried definition syntax:

    (define ((foo x) y)
      (list x y))
https://www.gnu.org/software/guile/manual/html_node/Curried-...

Or you can bind "lambda" to another, shorter identifier.

But anyway, Lisp's goal isn't to be extremely terse, so it doesn't bother me.


Lisp is not about microsyntax and code golf. If you worry about the number of characters and that is "the real problem" with Common Lisp, Perl or J may be a better choice for you.


In Clojure it's #(+ 2 %), pretty compact if cryptic. Actually no reason you couldn't make a reader macro in CL to look similar.


Slightly less cryptic would be (partial + 2).


Partial application and currying are two different things.


That's correct, I'm just responding to the specific case here.



You could make a macro that does the currying for you. You'd have to apply it explicitly, as opposed to ML, where it happens automatically. If you build the macro well, the generated machine code won't look much different from ML, though.


> I can't get past the lack of currying.

Say again? Currying may not exist as a special form, but adding it is utterly trivial: [0]

Which I guess is where Lisp shines. Modifying the language to suit your needs is easy. You aren't bound by the limits that many languages force upon you.

That being said, I wouldn't say that Common Lisp or Ocaml have the same userbase. In much the same way that C and JavaScript don't serve the same domain.

ML languages are about safety, and correctness.

Lisps tend more to be about speed of development, and flexibility.

That being said, as just about any paradigm can be implemented in a modern Lisp, and perhaps older ones as well, there are places where they can cross over with this want of correctness.

Racket is an experimental Lisp that grew out of Scheme, and is basically a family of languages, or a Lisp-ish language framework for creating other languages. Among the inbuilt languages, is typed/racket [1]. It uses contracts or inference to ensure type safety at compile time. Due to Racket's language features, if you add contracts, typed/racket can use, or be made use of, by other Racket languages, like lazy/racket[2] and scribble[3].

Other Lisps can have similar things, as you saw with currying, Common Lisp can have compiler hints, or even static typing [4]. Clojure has a ML style matching library [5], inbuilt.

Basically, where you might hit a "I'm approaching this problem the wrong way" in most languages, you can simply write a quick, simple, effective macro, and carry on, in most Lisps.

As to what you may think a Lisp looks like... See Wisp [6]. Which, arguably, isn't a Lisp. But it is basically just a bunch of macros on top of Guile (a Scheme).

[0] http://cl-cookbook.sourceforge.net/functions.html#curry

[1] https://docs.racket-lang.org/ts-guide/

[2] https://docs.racket-lang.org/lazy/

[3] https://docs.racket-lang.org/scribble/

[4] ftp://ftp.cs.utexas.edu/pub/boyer/diss/akers.pdf

[5] http://blog.klipse.tech/clojure/2016/10/25/core-match.html

[6] http://www.draketo.de/english/wisp


Adding 'curry' is relatively easy. Difficult is the idea of implicit currying: a function call with one argument less, returns a curried function.

    (mapcar (+ 1)
            '(1 2 3))
Above does not work in Lisp, because it has variadic function arguments and no automatic currying. One would always need to explicitly write that (+ 1) should not return 1, but the function 1+. In some functional programming languages it works, because + with one argument returns a one argument function.


Implicit currying sounds like a bad idea, at least for a Lisp.

Most Lisps show off inbuilt functions that have side effects, such as setf in CL or set! in Scheme.

Magic with side effects tends to be looked down on in the community, because it changes an incredibly simple syntax that is just about self-describing to one that has cognitive load in differing circumstances.

Take pity on whoever has to come after you.

My point was merely to show that it is possible.

It should be also possible to add implicit currying, by messing with a read macro, but I would ask that you don't.


Another consideration/angle is this. Suppose I call a three-arg function with only two args. Why should that curry in the Haskell way? That is just someone's design opinion. Suppose I want that two-arg call to return a function, but not the ordinary curried one. For instance, I might want the function to curry to a transduce kernel. Meaning, I can pass the resulting function to reduce, such that the reduce now performs a mapping through the original function. In other words, this:

  (map (lambda (x) (foo 1 2 x)) list) <--> (reduce (foo 1 2) list)
In mainstream languages that have implicit currying, we're stuck with the one and only currying semantics.


But we can easily write a function plus such that (plus) returns (lambda (x y) (+ x y)), (plus 1) returns (lambda (y) (+ 1 y)) and (plus 1 1) just returns 2.

And we can make a function writing macro to simplify writing such a function.


True: but still you have to decide at definition time what kind of function it is.

Plus it adds bloat to the function. Ideally the currying would be a task for the compiler/interpreter.


Definition time is a big deal in static languages. For us, that could be while the app is running. I want that three arg function inside that running service to curry if called with two args, with no restart. Poof, done.


Fair enough ; the strengths of Lisp languages lie elsewhere : macros and rapid prototyping. Having said that, Clojure is also pretty good for FP as you describe it.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: