Ask HN: If you could only code with one language which would it be?
My friends and I are discussing which language we would choose if we only could code in one language for the rest of our lives. Some arguments have been made for c++, SQL, Java, python and JS
52 comments
[ 2.6 ms ] story [ 101 ms ] threadIt has all you mentioned:
- C++ (Strong OOP + Static typing)
- SQL (LINQ)
- Java (Well, no comment here)
- JS (dynamic, async/await)
By having the widest array of hardware devices to build on, I’d have the opportunity to solve the widest scope of problems.
Or Stackly, a language I've been writing for nearly two years now. Like C, it's performant, compiles to tiny binaries (it maps fairly close to assembly under the hood), but unlike C it has a ref-counting GC, compile-time and runtime type safety.
I did used to care a lot more about community adoption or libraries, but less so today. If you're not dealing with protocols or encryption, and there is no library, and you can spare a month or two, then writing the library isn't a hard task. First fortnight to implement, six weeks to fuzz n fix.But then, I've been forced to work with all sorts of estoric languages in my work, from PL1 to FORTH to Brainfuck, so my experience is unlikely to be the norm.
Stackly isn't public yet, and probably won't be until I finish the type-checkers.
[0]: http://p-cos.blogspot.in/2014/09/why-i-like-common-lisp.html
Common Lisp / Scheme are great to learn about programming languages themselves. think learning about closures in CL vs in ObjC and you will realize how simple concepts become awkward and secondarily builds your taste so you know engineer who prides in knowing C++ complexity is a fucking idiot.
Personally, CL has helped me quickly learn hard subjects (3D graphics), prototype software for open ended problems, etc etc.
And any given day, give me a language with live editing and updates, I hate the edit/compile/test cycle.
Other languages have gotten nice repl environments in the last year, but the integration with the language is never as good as lisp repls.
I'm curious what kinds of projects would cause you to reach for common lisp over scheme and vice versa. Also would be interested in any other thoughts comparing the two.
Scheme has the benefit of some particularly excellent introductory texts (Little Schemer, Seasoned Schemer, Structure and Interpretation of Computer Programs, How to Design Programs). The first two are not free, but are great introductory texts (and can be used with Common Lisp with some adaptation, same with the other two), the second two are freely available online.
Scheme is also a conceptually "purer" functional language than Common Lisp. Depending on your background and feelings towards other functional programming languages that may have some appeal to you.
Common Lisp has a comprehensive standard library via the components described in The Common Lisp HyperSpec. With Quicklisp and other things you can easily add packages to your projects extending your environment to provide support for nearly anything anyone has ever written a CL library for.
From a technical perspective, Common Lisp can be a more pragmatic language in many ways. It allows you to drop into base procedural code, or write functional code. It has a built-in object oriented system (CLOS) that has a lot of fantastic features. Much of this can be added to Scheme, and may be available through an SRFI, but is not baked-in. If you're interested in implementing object systems, Scheme can be a fun language to use for that purpose.
Setting aside those, another big difference is one that sometimes causes heated debate, but for me is just a thing to keep in mind. Scheme is a lisp-1, Common Lisp is a lisp-2 (really a lisp-n). Scheme has one namespace that everything uses (though you can encapsulate things inside of closures and such). What this means is that at the top level your functions and variables share a namespace. If you make a function f and a variable f only one of them exists. This leads to some cleaner expressions when using a functional style as you can just use the variable name in the function position. In Common Lisp you may have to use something like (apply #'f some-param) when f is a variable holding a function reference. This is a minor thing, in my view, but if your intent is to explore the functional side of lisps it can be nicer to use Scheme. Common Lisp has separate namespaces for functions, variables, macros, and other entities (I'd say objects but I want to avoid confusion with the sense of object-oriented programming objects).
Both have module or package systems for organizing projects. Both have good testing capabilities baked in or that are easy to add on. Both have active communities (with a decent amount of overlap between them).
Regarding the CL library, I'm inferring that you feel the SFRI modules aren't as comprehensive as the CL ecosystem. Is that accurate?
Also, what is your experience with the available tooling, performance and language interop options for each lisp?
Sorry for the 20 questions!
Here [0] is a list of the various SRFIs. To answer your first question, I'm going to first digress.
The history of Lisp began in McCarthy's lab with him and his students. As the language grew in adoption many variants developed, there was no real standard (as opposed to Ada, Fortran, Algol, and others that developed around standards groups). Scheme was an attempt at something like a minimal, lambda calculus-based, lisp. There's more to its history than that, but that sense of minimalism is crucial to the Scheme approach. Common Lisp was developed in the 80s as a way to develop a common lisp that brought together many of the academic and commercial variants in use at the time.
Why is that important? Commercial and academic lisps and a standard merging them was necessarily large. There were a lot of decisions made that look like over engineering, until you think about the multitude of systems that it had to run on. Common Lisp's file path handling can understand how any operating system might present file paths. The DOS/Windows formatted string, a Unix/Posix path, whatever VAXes do, IBM mainframes, etc. Very very useful. But not everything made it into the standard, and it hasn't been updated. As a community Common Lisp implementations do stay near each others' approach when extending their implementations to account for things like Unicode or multithreading, but it won't be the same. Some libraries work by creating an interface and wrapping the implementation specific code behind it, giving you a more portable way to accomplish your tasks. CFFI [1] is one such interface, as an example. Common Lisp had to include some functionality so that people who'd written Maclisp or Starlisp or whatever could port their code with minimal alterations,
Scheme never aspired to that. And that's where the SRFIs come in. SRFIs aim to take common behaviors that may have various conflicting implementations by people over time and provide a consistent interface. Every SRFI is not available in every Scheme implementation, and some may have portable implementations written in mostly pure Scheme, while others need to be tailored to a specific Scheme implementation. This also means, though, that unless a library is written in pure Scheme (and many are), they are less portable in the Scheme world than libraries written in Common Lisp.
Chicken Scheme is the one I used the most besides PLT Scheme which developed into Racket. It has a pretty comprehensive coverage of the SRFIs (or did at the time, should still) and a good sized set of available libraries (called eggs [2]).
When it comes to being able to get things done and write a program, both Scheme and Common Lisp are equally effective in my view. But they do have differing philosophies in how they grew to be where they are today.
You may have to write more code yourself in Scheme. But that's not always a bad thing. In Common Lisp I often found a function already existed to do exactly what I had in mind. I mean, yes-or-no-p [3] already exists in Common Lisp. It was considered sufficiently common and useful to make it into their standard. Scheme doesn't have that and wouldn't have that. So it goes to philosophical differences and differences in the goals of the two communities.
==============
Regarding tooling, I've not spent too much time trying to do performance profiling. But CL has a lot of that baked in. This is part of its kitchen sink appeal. It has a really good debugging system built in. You can change the value that caused an error and resume from that same point. Scheme debugging is more implementation specific, I generally did it by way of the REPL adding a line or function call at a time until I found the error.
CL has baked in trace and time functions which can be used for performance profiling (how often does a function get called, how long does it take to execute an operation, etc.). For Sc...
Common Lisp was mainly designed to be a common successor to Maclisp, replacing Spice Lisp, NIL, Lisp Machine Lisp, S-1 Lisp. Those were all relatively similar Maclisp descendants.
> Commercial and academic lisps and a standard merging them was necessarily large.
Common Lisp is large, because it is based mostly on Lisp Machine Lisp (aka ZetaLisp), which was already large. Which was an expanded version of Maclisp - which was already large. Lisp Machine Lisp was simply the Maclisp successor with the most work put in already.
Stuff like pathnames also came from Lisp Machine Lisp, where the Lisp-written OS interfaced to several outside systems with different pathnames. It was thought to be useful in portable code, since Common Lisp code should run on many different operating systems with different file systems.
> Scheme never aspired to that
R6RS Scheme and R7RS Scheme Large were/are attempts to standardize slightly larger portions of the Scheme language.
If I had actual time limits, python.
A (non-scientific) look at Wikipedia puts Python's first public release in 1991, and PHP in 1995. But Python's roots actually go back to the late 80s, while PHP was started and released in 1995. PHP was really originally created as a collection of helper functions, not a full programming language.
If you know Java, it takes a week or so before you're comfortable, and after that it's magical. All of the boilerplate, all of the tiny little frustrations and bad ergonomics, are just gone.
IntelliJ is a fantastic IDE, too, and I didn't bother picking it up until I started playing with Kotlin.
Kotlin compiles to JVM bytecode, and it runs everywhere I'm currently deploying applications. It can be mixed in with existing Java baselines, and interoperability is almost perfect.
Kotlin also cross-compiles to Javascript, and there's a new Native target, which I haven't played around with yet, but seems like a decent answer to Go's statically compiled, platform native, single binary output.
Lastly, there's an experimental feature that allows you to write one source base and target native, the JVM, and browsers. That finally gives me a single language that I can run anywhere that isn't Javascript.
It can do (most) everything that C can, but is (usually) a lot easier to work with.
I usually wanted to code in one language to save learning many languages to an expert level. Also it felt good to have a uniform code on various levels of the system - something which today in JavaScript world is called "isomorphism". Then I realized I'm really doing different things in different layers, and in a big language I'll perhaps use different parts of the language - effectively several languages. For a small language I'll probably be creating necessary constructs first - again, different for needs of different layers - and then would use those.
Now I think it's better to have the ability to adjust language after the task, and here Lisps look rather good. Even more, with security in mind I want to use not only "language suited for the task", but "language minimally suited for the task", so it's hard to use it incorrectly and easy to reason about abilities of the particular software layer. Here, again, Lisp-like languages are a good platform. Some others languages suitable for DSL creation (Haskell? Tcl?) can also be good.
But please don’t let it come to this. I’m happiest as a polyglot.
My reasoning is that this is one language that you can use to write apps for MacOS and iOS possibly sharing some codebase. It doesn't run on VM, so should be faster. Swift Playgrounds got me interested in this especially the fact that it seems to work with a lot of toys like Mindstorms. It might even evolve to support server side dev. So I see it as decent investment.
Also people in Apple's ecosystem seems to spend more money on apps even with smaller market share of Apple. And really want to write apps that do something interesting as I am really bored with another CRUD webapp with pretty UI.