13 ms·
A difference between Haskell and Common Lisp
- catnaroek 11y agoThe difference is not one of "philosophy", but rather of using the right types. Haskell's so-called "list" type constructor is actually a type constructor of streams. Unsurprisingly, streams are much better than lists if you want to do stream processing.
- wyager 11y ago>Haskell's so-called "list" type constructor is actually a type constructor of streams. Most papers consider streams to be lists without a terminal constructor. Haskell lists are a superset of streams. All streams have infinite lengths, but only some lists have infinite length. (Again, this just comes down to definition, but this is how most functional papers describe it.)
- catnaroek 11y agoIn chapter 4 of his book, Okasaki defines a type of both finite and infinite streams (which is really just the type of Haskell "lists"), and, in the remainder of the book, he only uses finite ones. A stream of infinite length, which you call "stream" without qualification, is what I call "a function on the natural numbers". (Well, up to isomorphism.)
- chas 11y agoSpeaking of things that the Haskell type system lets you make explicit, the isomorphism between streams and functions from the natural numbers means that streams are "representable functors" in the jargon of category theory. [1] Knowing that a data type is representable allows you to immediately build a bunch of other interesting structures on the data type. [0] [0] http://covariant.me/notes/rep-functors.html http://covariant.me/notes/rep-functors.html [1] https://pamiz.wordpress.com/2014/02/13/the-functor-of-infinite-lists-is-representable-by-natural-numbers/ https://pamiz.wordpress.com/2014/02/13/the-functor-of-infini...
- catnaroek 11y agoNitpick on both links. They say `alpha . beta = id = beta . alpha`. This is wrong: `alpha . beta` composes to a different `id` from `beta . alpha`.
- tel 11y agoThat's one way of doing it, but most formally I think it has to do with whether you're considering the list type in a "data" sense or a "codata" sense. In either case you might be talking about the same "shape" of data, but streams are defined under observation as compared to construction.
- fnordsensei 11y agoIs this really a philosophical difference? Granted, I've only used Clojure as far as Lisps go, but composability seems to be something that's emphasized. Rather than (remove-if-not #'p xs :count 5 :start 3) It seems to me like most Clojure users would do something like (->> xs (drop 3) (filter p) (take 5)) which is much closer to take 5 . filter p . drop 3
- icholy 11y agoUnlike CL/EL, Clojure is lazy by default. So it's closer to Haskell in that regard.
- catnaroek 11y agoMinor nitpick. Technically, this: #(->> % (drop 3) (filter p) (take 5)) Is equivalent to: take 5 . filter p . drop 3
- fnordsensei 11y agoIs take 5 . filter p . drop 3 An anonymous function call in Haskell? I don't know the language (although it's on my to-learn sequence).
- grabcocque 11y agoYou have to be clear about which Lisp. Clojure and Scheme are Lisps, and their focus is very much towards simplicity and the use of combinators. The complex, do-it-all-in-one-huge macro is a Common Lispism, not a Lispism. Secondly, Clojure, like Haskell, is focused on sequence abstractions, not lists. Sequences can include collections, streams, observables, sockets and many other kinds of process that can be modelled as an event stream.
- agentultra 11y agoCommon Lisp isn't technically focused on lists as you imply. There are plenty of libraries which provide "sequence abstractions" if that's what you need. Common Lisp provides structs, arrays, and even a hash table implementation. The methods of abstraction are transparent in Lisp.
- lispm 11y ago> Clojure and Scheme are Lisps Basically new languages with strong Lisp influence. > very much towards simplicity Scheme maybe until the early 80s. Later it grew to Common Lisp size and beyond. See Scheme R6RS which is a complex language with tons of features and still underspecified. > The complex, do-it-all-in-one-huge macro is a Common Lispism, not a Lispism. Not really. Macros appeared in Lisp in the early 60s and many Lisp dialects have used it in complex ways. For example the 'famous' LOOP macro of Common Lisp actually was developed in Interlisp in the early 70s (as a part of 'Conversational Lisp' / CLISP), redesigned in Maclisp/Zetalisp and then brought into Common Lisp. The original Common Lisp in 1984 did not even have that macro in the language description, it was standardized several years later - after a search for a better alternative failed.
- PuercoPop 11y agoMinor nitpick. The loop at the end could make the range by 'for i from 1 upto 4' instead of a list literal to more similar to the haskell code.
- kiiski 11y agoThat wouldn't be the same. The point was filtering a list (which could contain anything, not just a sequence from 1 to 4). The example seems a bit strange to me. Why does it have the check for i being less than 5, when the problem is "get all elements _greater_ than 5, then just the even ones of that set.". Was it supposed to be "remove all elements greater than 5..."? (edit: although the code would then only work for sorted lists; I think it'd be better to just loop over the whole list and have "(and (< i 5) (evenp i))" as the condition for collecting)
- iamcurious 11y agoI'm confused by the last example. There are no elements greater than 5 in the list (1 2 3 4). Also, I'm not sure why they used takewhile instead of filter in the last haskell part.
- Xophmeister 11y agoI assumed it to be a typo; i.e., it should say "less than". `takeWhile` is different to `filter` in that is returns the input list until some element matches the predicate, whereas `filter` returns all elements that match the predicate. For example, if the predicate is `(< 5)` and the input was `[9, 2, 3, 6]`, then `takeWhile` would return an empty list, but `filter` would return `[2, 3]`.
- gkya 11y agoHe should have used a better input, though, maybe 1, 2, 3 ,4 ,5. With the given input it's a bit confusing, it's equivalent to filter . even [1..4]. Also, the Lisp loop should have been checking for (>= i 5), to be equivalent to the Haskell example.
- iamcurious 11y agoYes, but I expected to see (filter even . filter (< 5)) [1..4] given the specification and the lisp counterpart. I do not fully understand the lisp code, so I can't be sure what is a typo and what is an erroneous assumption from my part.
- vorg 11y agoHaskell has strong typing and lazy evaluation, which makes it easy for functions to take only one argument at a time. Although a function could take a tuple parameter, it's usually rewritten to take each component of the tuple as a separate parameter, which makes the strong typing and built-in currying simple, higher structures like monads possible, and a syntax to suit this style. Lisp functions and macros OTOH must be variadic to enable the homoiconicity of the language. It's therefore much more difficult for parameters to be typed, or to curry them. These two styles make Haskell and Lisp mutually incompatible, unless they use clunky addons like Template Haskell macros or Typed Clojure annotations. The pure form of each language, however, is based on two mutually exclusive foundations, i.e. strongly typed auto-curried parameters vs variadic untyped macro parameters. The poster child of each language, i.e. monads and macros, thus also don't mix well with each other.
- pron 11y ago> higher structures like monads possible What is it about the features that you mentioned that makes monads possible? Lisp (or at lease Scheme and Clojure, which I'm familiar with) make monads trivially possible -- just as they are in Haskell. They're not as useful because those languages have other mechanisms that make monads largely unnecessary, but they're no less easy to express.
- lmm 11y agoWell you can't have implicitly resolved typeclasses without a static type system. You could pass around explicit dictionaries with all your values or some such, but most of the value of explicitly sequencing relatively minor effects is only there if you have an extremely low-overhead way of doing so, and a system that can verify the correctness of that sequencing at compile time.
- pron 11y ago> Well you can't have implicitly resolved typeclasses without a static type system. Yes, but that wasn't the feature mentioned in the GP comment. He mentioned lazy evaluation, with currying, static typing and monads as a consequence. But if monads are just a consequence of the type system, then I understand what he meant. > but most of the value of explicitly sequencing relatively minor effects is only there if you have an extremely low-overhead way of doing so, and a system that can verify the correctness of that sequencing at compile time. Well, extremely low-overhead of just about anything is certainly achievable with modern JITs[1]. > and a system that can verify the correctness of that sequencing at compile time. Why is that necessary? You might as well presuppose the necessity of types :) or, alternatively, say that you need a type system to verify that your monads are truly monads (i.e. obey the monad laws) at compile time, something Haskell doesn't do, either. [1]: https://twitter.com/ChrisGSeaton/status/619885182104043520 https://twitter.com/ChrisGSeaton/status/619885182104043520
- srott 11y agoOr you could use Shen and spend less time philosophizing http://www.shenlanguage.org/ http://www.shenlanguage.org/
- devty 11y agoCould you elaborate? What about shen makes you say this?
- catnaroek 11y agoShen is like C++ in that it's bolted on top of a less typeful language (except this time it's Common Lisp, rather than C), and is "type safe" as long as you don't deliberately use a number of escape hatches.
- qwertyuiop924 11y agoThat's wrong in just about every respect. Shen is a PLATFORM INDEPENDANT (not clisp based) language with an emphasis upon functionalism, a novel and very powerful type system based on sequent calculus, and OPTIONAL type checking, IF you want it. It is platform independant because it is built upon an incredibly simple lisp that you can build an interpreter for on top of almost any platform, so long as you can guarantee TCO. It really is lisp flavored haskell.
- catnaroek 11y agoThe point to type checking is protecting abstractions, and it being "optional" reduces its value to zero.
- qwertyuiop924 11y agoWrong. Sometimes type checking is uneeded. In addition it can make prototyping easy: Write the code, add the annotations later. Of course, you need some discipline to make it work, but of what good practice isn't that true? And if you really hate it, just type (tc +) at the start of your code. Magic!
- m0skit0 11y agoIMHO whoever wrote this article should change the title to "A philosophical difference between Haskell and Common Lisp". There are a lot of LISPs and not all of them follow Common Lisp's philosophy.
- deleted 11y ago[deleted]
- nsfyn55 11y agoI'm not sure I agree at all with the premise of this article. Languages are a syntax they don't have "Philosophies." They may have design elements that reflect or support the philosophies of their designers(e.g. functions as first class citizens), but the author doesn't provide critique on what they believe these to be instead looking at a few std lib functions that some yahoo implemented. Look at something like java. Java has a core set of design elements. It was built by a person with some philosophical leanings("Everything is a class", "Checked/Unchecked exceptions being different things", "no first class functions") Then it has standard libraries(i/o, collections, threading/synchronization) each of these was built by a person with there own set of biases and understandings. The original Collections implementation has lots of mutable data structures then later Josh Bloch decided that he didn't like that anymore and stopped adopting "immutability" as core design philosophy. Immutability was not previously considered important when evaluating a java implementation. What you end up with is a mish mash of different opinions that only gets more different as you go. Some 3rd party libraries like Guava didn't jibe with the Philosophical leanings of the language itself and looked for work arounds. They went as far as to create their own implementation of functions as a first class citizen in a language that was expressly designed to omit them. Some commonly used Android libraries do this as well. My point here is that "language" can mean lots of things. It can refer to the language itself, its run time, the syntax+runtime+community. What is idiomatic and on, on, on. People and groups of people have the philosophies. I'd like to see the author point to something a little more critical about the differences between these two concepts. This premise is a little muddled.
- deleted 11y ago[deleted]
- ZenoArrow 11y agoPerhaps it's just me, but I don't see that Haskell and Lisp are that similar, other than... 1. They're both programming languages. 2. They both allow you to pass functions as arguments to other functions. Am I missing something here? Why are the two linked? Is it because Lisp is seen as the birthplace of functional languages (because of point 2)?
- lmm 11y agoYeah, people talk about "functional languages" as if they're a unified thing. IMO the differences between the strongly typed ML/Haskell tradition and the Lisp tradition are as big as the differences between either and "OO languages" or "imperative languages", but that's not the way it's usually presented.
- qwertyuiop924 11y agoThat's nonsense. Lisp in the functional style vs. Haskell is two different implementations of what are at their essence the same ideas. However, lisp is multi-paradigm, which complicates the comparison somewhat...
- ZenoArrow 11y agoWhat are the essential features that they share?
- qwertyuiop924 11y agoThey're both primarily based on lambda calculus. Although haskell on typed lambda calculus. They both emphasize purity over mutations. They both are designed to make higher-orderisms idiomatic. But the point isn't so much the features they share. I'd be the first to admit that Lisp and Haskell are very different. But functional programming in both is very much based on the same ideas. Claiming that they're as different as OO vs imperative is like saying that washing dishes by hand is as different to using a dishwasher as football is to baseball.
- 11y ago
- lispm 11y agoActually Common Lisp supports a gazillion of different programming styles. The version with small functions, similar to the Haskell version: (subseq (remove-if (complement #'numberp) (butlast list 3)) 0 5) In above Common Lisp code, we use four different functions which do one task: * subseq sequence start &optional end => subsequence * remove-if test sequence => result-sequence * complement function => complement-function * butlast list &optional n => result-list For different approaches see Common Lisp libraries like Series or Iterate... Iterate is LOOP on steroids. http://series.sourceforge.net http://series.sourceforge.net https://common-lisp.net/project/iterate/ https://common-lisp.net/project/iterate/ > This is known as composability, or the UNIX philosophy. In Lisp a procedure tends to accept many options which configure its behaviour. Check out the UNIX man for tail, grep, ... to see how strange above quote about 'unix philosophy' is. In reality Unix commands are programs with obscene amount of configuration options, sometimes piping data as text around, sometimes glued together by strange shell languages...
- jfb 11y ago> Check out the UNIX man for tail, grep, ... to see how strange above quote about 'unix philosophy' is. I wish someone would take that hoary old meme out behind the barn and put it out of our collective misery.
- qwertyuiop924 11y agounix commands may not be the pinnacle of minimalism, but the most important thing UNIX taught is about composability. Yes, I did recognize the irony of the fact that the common lisp example was much more like a unix command line than the haskell example, but if you're talking about composing (relatively) small commands, unix is still a pretty good comparison.
- nightski 11y agoFor being a pinnacle it is really unsatisfying. It tells you very little about how or what programs can be composed. It's either trial and error or reading man pages. In addition every program has to be written to take in anything as there are no constraints. Function composition via types is a much more satisfying take on this problem. It's too bad Unix is regarded as the holy grail of this technique when it barely provides a passable implementation of it. Text just isn't a great medium for IPC.
- rusabd 11y agoHaving worked on one of the largest common lisp projects I have to say this is spot on. And monolithism it's not just visible on level of functions it's visible on higher level. Common lisp nudges you to write monolithic applications and it's crucial to keep many details in one head (in case of big project - many heads). Also I think Clojure is scheme, so overall approach is different.
- catnaroek 11y agoNo, Clojure isn't Scheme. Scheme has hygienic macros and pattern matching on syntax, which in turn Racket's `syntax-parse` library makes even better. Clojure... well... has that ugly `defmacro` kludge. EDIT: I was being kind of unfair. Clojure also has kickass collections in its standard library, which Scheme doesn't have.
- qwertyuiop924 11y agoScheme is a lisp. Also, yeah, Common lisp probably would push for monolithism. Lisp Machine Lisp, upon which Common Lisp was based, was very much built upon The Right Thing, monolithic tradition of MIT, ITS, and all of the lisp machines.
- agentultra 11y agoCommon Lisp has these kitchen-sink functions and macros because it was a standard developed by a committee whose goal was to incorporate several popular implementations of Lisp that each had several decades worth of of cruft. Lisp was big enough business at the time that having several mutually incompatible versions of Lisp was making knowledge sharing and business difficult. And having a standard was important for many reasons. update: spelling and... The real philosophical differences between Haskell and Lisp are basically apples and oranges. Lisp's composition strategy is the recursive application of symbolic expressions... the famous EVAL and APPLY. I don't know enough about the theoretical underpinnings of Haskell to make any kind of apt comparison but my intuition suggests that to do so would be moot. They're just too different.
- catnaroek 11y agoThe theoretical foundation of Haskell is a typed lambda calculus called System F-omega. GHC Haskell has some additional features that are inexpressible in System F-omega.
- agentultra 11y agoThere you go. Lisps tend to implement the untyped lambda calculus. Not too surprising or interesting...
- catnaroek 11y agoLisp is far from any lambda calculus, even the untyped one. Lambda calculi don't have variadic functions, or any means to inspect their own syntax. Lambda abstraction is called abstraction for a reason - you can't inspect the expression inside. Lisp is really its very own kind of thing, which can be both very interesting (if you care about extensibility) and very irritating (if you care about abstraction). Racket, a dialect of Scheme, is the only serious attempt so far at providing an extensible language that protects user-defined abstractions.
- agentultra 11y ago
- jinfiesto 11y agoI'm not entirely sure that this is due to philosophical differences. The fact that Haskell is lazily evaluated makes writing functions that do only one thing much easier, since there is no performance hit for writing code like: take 5 . filter (not . p) . drop 3 In a strictly evaluated language. This would involve iterating over the list three different times. (Kind of not really, since take 5 isn't going to be that expensive.)
- deleted 11y ago[deleted]
- rjayatilleka 11y agoNot really. It doesn't matter that Haskell is lazy by default, so long as you called the function on a lazy stream data structure. Clojure is strict but it can do this efficiently as well, I think.
- Veedrac 11y ago> there is no performance hit for writing code like: > take 5 . filter (not . p) . drop 3 That doesn't seem to be the case in my testing. Writing this out as an explicitly recursive function sped things up 3x on GHC -O3. (Dropping 10^8, taking 10^7.) FWIW, I also tested on Rust, Python (PyPy3) and Java (OpenJDK) with iterators, iterators and streams, respectively. Python and Java were about as fast as the manually recursive version, and Rust was two orders of magnitude faster than that. And half of the time in Java was spent boxing, because Java can't reify types away like Haskell. It's true these are all using opt-in laziness, unlike Haskell's lazy-by-default, so it's not a fair comparison. But I also see little reason to believe that there's "no performance hit", given that that's exactly what I saw.
- dgreensp 11y agoTitle is misleading; this is about Common Lisp in particular.
- Grue3 11y agoWell, Common Lisp is Lisp. Scheme, Clojure etc. are lisps, with lower-case "l" ;)
- qwertyuiop924 11y agoThat's just wrong. Clisp is the most popular lisp bearing the name, but the LISP, Lisp, or lisp family includes Scheme, and Clojure. if you just refer to Lisp, you may be referring to Clisp, the lisp family, or maybe even LISP 1.5, the lisp equivalent of V7 unix.
- lispm 11y agoClisp is an implementation of Common Lisp. Common Lisp includes a core of the original Lisp. Lisp programs from the 60s can either be run or ported to Common Lisp with little or no effort. Clojure is FULLY incompatible with any other Lisp dialect or Lisp derived language. Porting code means 'rewrite'.
- qwertyuiop924 11y agoOh. Sorry. I was abbreviating Common Lisp. Whoops. Anyways, that's like saying that Go, Plan 9 C, D, Java, and Cyclone aren't in the C family, because you can't run ANSI C '99 on them and have it work. The Lisp family is diverse.
- lispm 11y agoSee C++, Objective-C and a bunch of other languages actually in the C family. They all share C. Real basic C. 'The Lisp family is diverse.' You interpret it in a way to make it practically meaningless.
- hardwaresofton 11y agoThe biggest difference between Haskell and Lisp is that Lisp is multi-paradigm, while Haskell is not. Haskell is more opinionated, and makes a bunch of decisions for you (that you can choose to work around/sugar/hack until Haskell looks like something else/does what you want). All the other things that Haskell comes with - strong typing, monads, lazy evaulation, can be written into common lisp, but whether you need them is often questionable.
- reikonomusha 11y agoThey can't be written in Lisp well, or at all. Strong typing with inference can't be bolted on. Monads that take advantage of this typing can't be bolted on. Changing Lisp to have lazy semantics across the board ain't gonna happen. You might be able to write a Haskell interpreter from scratch that interacts with the Lisp environment, but you can't feasibly transform Lisp itself.
- qwertyuiop924 11y agoNo, but you can build a new lisp :-D. Or just use Shen.
- sklogic 11y agoWrong. You can build an entire Haskell implementation as a thin, transparent layer on top of Lisp and then do anything Haskell can do.
- reikonomusha 11y agoMy post literally says "you can build a Haskell interpreter which interacts with Lisp".
- sklogic 11y agoAnd this is irrelevant. Interpreters suck and are not interesting at all. I am talking exactly about transforming Lisp into Haskell. Fully or gradually, it does not matter.
- qwertyuiop924 11y agoAnd us schemers would express it like this: (cut take 5 (filter <> (drop 3 <>))) or, without the cut macro: (λ (pred list) (take 5 (filter pred (drop 3 list)))) And yes, in most schemes, you can use λ as a synonym for lambda. Sometimes you have to define it first, though. Anyways, that looks a lot like the Haskell to you, doesn't it? It doesn't have the laziness, but other than that... Oh! Oh! I almost forgot! you can also use the thrush combinator, if you use the clojurian package: (λ (list pred) (->> list (drop 3) (filter pred) (take 5))) And yes, I think this is all the ways you can do this in scheme. CHICKEN specifically. With some various macros packages. Oh, wait! I forgot we have function composition, too, but you'd have to use lambda or cut constantly to make the thing work because we don't have currying. So these are all the ELEGANT ways to make this in chicken scheme. With the cut SRFI. Or clojurian. And srfi-1, which is basically standard. and it's better than the examples given, because pred and list aren't pre-specified.
- nickpsecurity 11y agoIt's easier for people propping up one language over another language family to ignore the similarly easy ways to do things in prominent members of that language family. ;)
- qwertyuiop924 11y agoWell, yes. Lisp and CL are not the same. If anything, this has made me rethink possibly going over to common lisp, which I was thinking of for the larger stdlib, because SBCL is very fast, supports TCO, and you can disable case insensitivity.
- nickpsecurity 11y agoCommon LISP was made to smooth over the differences of the ancient LISP's plus standardize their situation a bit. If those don't matter to you, then the alternatives are often better. SBCL being one of them. If I get back into LISP, I think the Racket Scheme community looks like it's delivering most bang for buck in terms of what the tools can do after investing time learning them.
- jlarocco 11y agoSome of the Lisp and Haskell code examples aren't doing the same thing. For example, these two do completely different things: (remove-if-not #'p xs :count 5 :start 3) take 5 . filter p . drop 3 I don't like Haskell, and it's not immediately obvious to me how to achieve what the Common Lisp is doing, so I won't bother, but one way of writing the Haskell in CL would be: (loop for x in (subseq xs 3) when (p x) collect x into result until (= (length result) 5) finally (return result)) Another option would be to use subseq and remove-if-not, etc, but without lazy evaluation, the loop version will be more efficient. And though some Lisp people dislike LOOP, I like that it's easy to read, if not always easy to write ;-) About the topic of the article, though, to me this seems like less a philosophical difference than a result of Haskell not having easy to use default and optional parameters. There's currying, but it's not a great substitute, and it's a little awkward to use. I like the Common Lisp way, even if it's crufty at times, because the keyword arguments to functions like remove-if-not and sort are easier for me to use than chaining a half dozen functions. I don't have to think about whether I need to call filter before or after take or drop, etc. At the end of the day, it's a personal preference, though. Another advantage is that I don't have to "roll my own" for common idioms.
- malisper 11y agoLisp actually does have stream fusion, but it has it in the form of a library![0] [0] https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html
- deleted 11y ago[deleted]
- sklogic 11y agoThe main difference is: it is trivial to build an efficient Haskell implementation on top of Common Lisp, keeping interoperability with the rest of the system. And it is impossible to do it the other way around, to build a Lisp on top of Haskell.
- tunesmith 11y agoReal general question about function composition - when you're comfortable with it, how do you think it to yourself, or say it to yourself, like what's your shorthand mental model? I was able to really intuitively get comfortable with unix piping a long time ago, so instead of: take 5 . filter p . drop 3 I'd be thinking, ok, take a thing, drop the first three, grep (or whatever), now take the first five. It felt intuitive because each step solved a problem, and then I'd move forward into the future, and have to only think of one additional solution (head -5) assuming I did the previous steps right. Meanwhile, take 5 . filter p . drop 3 is in the opposite order, from right to left. Maybe I'm just saying that left association is easier to think about than right association. Don't you feel that weird recursive bump in your head, the increasing mental stack, when you are dealing with function composition and right association?
- klibertp 11y agoI like imagining functions as various pipes, the real world ones. Compose is that short pipe that connects two others. I imagine that the data comes from the right and needs to be output on the left of the function. I then can read such line (take 5 . filter p . drop 3) twice. Once following the order of creation (ie. we're laying our pipes first) and then the second time in reverse, following the data and function applications. That did the trick for me when I first learned about function composition coupled with (auto)currying.
- kazinator 11y ago> In Lisp a procedure tends to accept many options which configure its behaviour. This is known as monolithism, or to make procedures like a kitchen-sink, or a Swiss-army knife. This is the case in some areas of the Common Lisp language; it is not true of the Lisp, as a family of dialects. There are plenty of examples of Lisp functions or operators that just do one thing: `cons`, `car`, the `lambda` macro operator. ;; CL (remove-if-not #'p xs :count 5 :start 3) ;; Haskell take 5 . filter p . drop 3 ;; TXR Lisp: a dialect with ties to CL: (take 5 [keep-if p (drop 3 list)]) ;; Compose the functions using opip macro ;; (result is a function object): (opip (drop 3) (keep-if p) (take 5)) TXR Lisp's library functions don't have the :count, :start and whatnot. In fact, there are no keyword parameters, only optionals. If you want to default an optional on the left and specify a value of one on the right, you can pass the colon keyword to explicitly default: (defun opt (a : (b 1) (c 2)) ;; two optional args ) (opt 4 : 5) ;; b takes 1, c takes 5. The colon is just the symbol whose name is the empty string "", in the keyword package. It makes for nice sugar and has a couple of uses in the language. Note how in the defun it separates required args from optionals. (Anyone else cringe at "UNIX philosophy"; how silly! This is the Unix philosophy: let's reduce everything to a string in between processing stages and parse it all over again, with simplifying assumptions that it always has exactly the format we are looking for without actually validating it.)
- nutate 11y agoThat's the JWZ perspective, but if you add strong typing you can basically turn the "string" into "SomeType" and process records like that. If you look at languages with a |> operator they often act like that.