3 ms·
Thank you for the examples. I'm really curious to get to the bottom of this. My inspiration has been Erik Meijer, specifically his article "Confessions of a Use
by mwattsun 5y ago
Thank you for the examples. I'm really curious to get to the bottom of this. My inspiration has been Erik Meijer, specifically his article "Confessions of a Used Programming Language Salesman. Getting the Masses Hooked on Haskell" [1]. If I understand you correctly, you are implementing lazy evaluation in your when macro, something you call short-circuiting.
By dropping laziness in strict functional languages such as Scheme, SML, OCaml, Scala, and F#, the purity and semantic beauty of true functional programming is lost. When programming in Haskell, laziness is one of those things that you rely on all the time without noticing it; it is something you only realize once you miss it.
As for async/await, Erik led the effort to put it in C# and then Dart. I'm assuming he wanted to bring Haskell Continuation Monads [2] to the popular languages.
I'm building a hobby website, so I got into Blazor because I like C# and was interested in doing it client side. I gave up on it, not because of C# or bad implementation (it's actually great) but because Microsoft has a noisy and bureaucratic way of configuring code that drives me nuts. I went back to Dart because it is amazingly productive on the client side, but now this video [3] shared in this thread makes me think I should give ClojureScript a try.
[1] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.868&rep=rep1&type=pdf http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72....
[2] https://www.haskellforall.com/2012/12/the-continuation-monad.html https://www.haskellforall.com/2012/12/the-continuation-monad...
[3] https://vimeo.com/230220635 https://vimeo.com/230220635
- greggyb 5y agoI'm happy to chat more if it's helpful (contact details in profile). I'll be honest that I didn't get Lisp for a long time, and stilll wouldn't claim true expertise. I attempted SICP a few times based on the glowing reviews from folks I admire. One of my paths to tech was via Paul Graham's writings, so I had a bias for Lisp early on. Two parts here. First, and briefly a note on short circuiting and lazy evaluation. Second, and longer, a further discussion of Lisp macros. First. Short circuiting is a special case of lazy evaluation, but is the most common exposure to lazy evaluation among programmers in the C family of languages. It is the property of conditional and boolean operations that evaluate only the minimum necessary to return a result. (or true IGNORED) only needs to evaluate the first operand to or, true, because the result of the or is true regardless of the value of IGNORED. This is common to all mainstream strictly evaluated languages I am aware of. https://en.wikipedia.org/wiki/Short-circuit_evaluation https://en.wikipedia.org/wiki/Short-circuit_evaluation Macros allow you to implement lazy evaluation in an otherwise strict language, but this is not a comprehensive explanation of their utility. Second. I've watched pretty much all of Rich Hickey's presentations a half dozen times or more. Somewhere in the mix of more practice, another time through SICP, and watching Rich Hickey I started getting what homoiconicity means and the value prop of Lisp. It's simultaneously very prosaic and very deep. Macros are pretty simple: 1. Access to the AST of a chunk of code for arbitrary processing. 2. At compile-time. This is not exclusive to Lisps. I think pretty much any mature language offers a library or a built-in mechanism for getting at the AST of arbitrary code in that language. The "at compile-time" part is not necessarily built in to other languages. You absolutely can have a build system that has two (or more) passes, the first building a macro expansion framework and library of macros and the second processing the rest of your source code and rewriting the AST of the code in those files to do macro expansion. Where Lisps are different: 1. The AST of the language is represented directly in the core data structures of the language. An operation in Lisp[0] is simply a list whose first element is an operator (one of a special operator, a macro, or a function in most Lisps) and whose remaining elements are operands to that operator. This makes AST manipulation easier, because you use the same functions/abstractions/interfaces as in normal programming, because you're directly using familiar data structures. 2. Separation of reader and evaluator (interpreter or compiler or either/both). The reader transforms textual representations into in-memory representations of values and data structures. The evaluator only ever receives these in-memory representations. Thus, it's easier to do programmatic manipulation of an AST. You never have to do code-gen of emitting source code glyphs to feed into a compiler that expects text input. Once past the reader, everything is an in-memory representation, so we never have to get back to text. That said, it's trivial to get back to text if you want, because Lisps also include a printer which takes an in-memory representation and prints the glyphs that represent their readable (i.e., can be read by the reader) textual representation. 3. The evaluation model explicitly supports tagging some code as a macro to go through macro expansion before being evaluated "normally". 4. It is idiomatic to use macros to solve code re-use problems that cannot be readily handled by standard function calls.[1] These four Lisp traits are not exclusive to Lisp, necessarily. As I mentioned above, you could build a macro expansion and code generation library that is part of a build process for any compiled language. Eval, in interpreted languages, is as expressive as Lisp macros, but is incredibly poorly supported and unergonomic in comparison to Lisp macros. It's an ergonomic and feasibility thing more than a capability thing. That said, POSIX shell is turing complete, so you don't need anything like C# or Lisp or Haskell or anything else we've discussed to get your computation done. The reaction you might have to comparing shell scripting to Haskell is not dissimilar to the reaction an experienced Lisp programmer might have to comparing AST manipulation and code gen through a library to Lisp's macro facilities. [0]: Clojure, specifically abstracts the idea of callable, and there are more callable things than special operators, macros, and functions. [1]: Languages such as Haskell and OCaml continue to push the boundaries of what can be handled by "standard function calls" (which term I hesitate to use, given its imprecision). That said, given that macros allow the use of a turing-complete language to generate inputs to the evaluator, they allow you to implement any language construct that is computable without modifying the evaluation infrastructure.
- mwattsun 5y ago> homoiconicity... is simultaneously very prosaic and very deep That's what drives me on. That and Rich Hickey's enthusiasm and the sense that he knows something really valuable that I don't. I watch a lot of videos as well, so I can't remember who said that programming languages were user interfaces between human and machine and are an attempt to talk to machines to get them to do things. In that sense, programming languages are a lot deeper than syntax convenience or efficiency. A good language allows us to talk to machine at a much higher level, and I get from functional programmers that this is what they find so attractive. I will contact you if I get really stuck and when I finally get it just to share it with someone. Thanks! edit: I've heard a lot about SICP but never looked into it. I think it's time, now that I have time
- greggyb 5y agoI'll recommend the videos from the 1980s and this version of the text, which is set nicely and has working footnotes. Videos: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ https://ocw.mit.edu/courses/electrical-engineering-and-compu... Text: https://github.com/sarabander/sicp https://github.com/sarabander/sicp (links to epub and html versions at the top of the README)
- mwattsun 5y ago> I attempted SICP a few times I don't have a computer science degree and I think that has held me back some, but I try to overcome it by always trying to learn and never thinking I should already know something or feeling like I'm an expert. I don't have the patience at my age to go back to school and get a degree. That you attempted SICP a few times is a good sign that you don't give up and speaks to your success in finally understanding.
- bcrosby95 5y agoHomoiconicity is neat, but I think the real nice thing is the S-expressions and general lack of special keywords. It makes it easy to take a chunk of code in your source file and seamlessly send it to the REPL and have it work. Compared to Elixir (another language I like) - it is homoiconic but it doesn't have this feature. I can't, for example, take an arbitrary function definition and update or define it in the REPL - because all functions must be defined in a module. However, I like these modules because I think it helps enable discovery of functions - it's much easier to find all operations you can do on lists or maps in Elixir than Clojure. Also, I wouldn't necessarily call macros more powerful than functions. They're just different, and they enable different things. You cannot, for example, pass a macro around like you can a function. Personally I've never written them, but I use them every day because the core library defines macros. I view them as ways to let people smarter than me to give me tools I can use to write my programs. As far as driving you on, I don't think Clojure is the end-all be-all language. I happen to like it. I also like Elixir. I know people who really love other, more standard languages though. I don't think you need to force yourself to love or "get" Clojure, but playing around with it can expand your mind a bit, especially if you've never used an FP before. But if you want an FP that (IMHO) would be easier to transition to, Erlang was the first FP that I "got", afterwhich I tried Clojure again. The syntax is a lot more like traditional imperative programs which can ease the transition. Nowadays I would recommend Elixir over Erlang for several reasons: more modern with modern tooling, less syntax quirks, more active online community, lots of active development. For me, the main benefits of Clojure are (other FP languages have some of these too): 1. Sharable, immutable state as the default. 2. A set of higher order functions that can do all sorts of data transformation. 3. Being able to use the same language on the web in client + server (without having to use Javascript). 4. Clojure gives me the ability to, more than other languages I've used, target the level of abstraction that I need for the problem at hand, which removes a lot of cruft. 5. Lack of special syntax: helps enable REPL driven development, makes it easier to seamlessly adopt new features from other languages. 6. It's hosted on two ecosystems I'm very familiar with - Java and Javascript.