In an effort to help new people coming to Racket the platform and the language this is an invitation to share your experiences coming from other platforms/languages to Racket.
What surprised you when you came to Racket?
What was confusing and unfamiliar ?
Which language(s) were you coming from? (if relevant)
Prior to finding and using Racket, I had basic experience at school with MATLAB, Python and C++ (in all, about a couple college courses' worth of exposure in total). I don't program professionally. I jumped straight into reading through the Racket Guide and using #lang racket to do programming exercises, rather than going through HtDP or any other intro to programming text.
A handful of the things that surprised or confused me:
Parentheses have a meaning and aren't just for grouping -- i.e. what an s-expression is, and that alpha beta gamma is three expressions but (alpha beta gamma) is a call of the procedure alpha with parameters beta and gamma
The Racket Guide mentions this in passing but it didn't really stick for me until I ran into an error I didn't understand with code that looked like this:
(if (> x y)
("greater")
("less"))
which throws the error application: not a procedure; expected a procedure that can be applied to arguments. It took someone pointing out to me that ("greater") and "greater" mean different things, and that the first one is wrong because it expects a procedure name at the head of every s-expression.
Linked lists are only fast if you treat them right -- i.e. building and accessing them from the head and not arbitrarily indexing into them. I was used to languages where vectors and arrays are the primary collection.
Recursion and higher-order functions are cool, but the list comprehension forms are usually easier to understand and just as fast -- I spent a little too long building things out of manually-written recursion relations and foldr statements before I discovered for/list and friends. I think it was out of the novelty of using recursion instead of loops with the zeal of a convert.
Disclaimer
Wall of text incoming. I love Racket. It is the Programming language I know the best after Python. This experience is my own, and a lot of what follows below could be described as me issues
Which language(s) were you coming from? (if relevant)
My background is a biology major with a Masters in Bioinformatics. I learned to program in Python during my masters in my early twenties and I picked up Emacs as my text editor of choice. This is the perspective of someone who is working as a data scientist, was not a CS major, and had only 1-2 years of programming experience at the time that they learned about Racket.
Emacs (like it has been for so many others) was my gateway to Lisps. Unfortunately I found it a little to difficult for me to learn at the time so I started looking for something else. I had heard about Common Lisp so I tried to learn that. I picked up Clisp initially and worked through Land of Lisp (I like games!) but fell off after awhile. I played around with other Lisps for awhile, SBCL, Guile, Chicken etc. I eventually found the How to Code Simple Data course on edX with Gregor Kiczales in Racket. It was a gentle enough introduction that after a doing a few modules, I finally felt like I was grasping Lisp. I also felt like I was learning some good design principles for programs in general. Since then, I've used it on and off for the past 4 years, but I've picked it back up in earnest after 8.0 and the full transition to Racket on Chez.
What surprised you when you came to Racket?
The documentation was very good. Having learned Python, there were always people around me I could ask for help, or great YouTube videos I could watch. I almost never had to learn to read documentation to figure out how things were done. I was initially intimidated by it when I would see all the annotations for a function (number? pair? is-a? frame%)? I still don't always understand it, but I find it much more readable now that I did a year ago.
It runs on so many things! M1 macs, Linux, Windows, with pre-compiled binaries. The only other Lisp I've had an easy time getting set up on multiple OSes was SBCL.
It's got a gui in its standard lib, and I sort of understand it. I've always been interested in writing native desktop apps, and I've been able to write a few simple ones in Racket. Also MrEd for a wysiwyg gui editor
There is a discord. This has been game changing for me. I never had much success getting answers on the freenode chat (pre libera chat debacle) and the last two times I've used the libera chat no one has responded. But the Discord has been super helpful and is very active. I don't have any Racket mentors locally so it's nice to be able to talk to people about Racket, or ask questions.
PasteRacket (it's awesome)
The tooling. Scribble, Raco, Frog. You can create an entire pipeline from the documentation to showing off your programs on your blog all in Racket.
What was confusing and unfamiliar ?
I would google for a function, I would click the link to the docs, and there was no example. As someone who was still relatively new to programming and dipping their toes into Lisp, I would immediately feel lost after this.
A lot of Racket documentation is very academic. I understand that the reference is supposed to be much more technical, but as a beginner, when the guide doesn't have the examples you need and the reference is a wall of text, it's super intimidating. I never learned BNF, Sometimes I feel like I need a PhD in programming language dev to understand it, or that I'm just not smart enough to ever get it, or that I need a reference for how the reference is written, and what concepts I need to know before it will be accessible to me. I'm just some guy who likes to program small stuff in there spare time, I'm not even sure I can call myself a programmer since most of my work work is just scripts and data munging and stuff. I don't have the credentials of some of the awesome people I see steering the project, and making cool stuff, and it leaves me with self doubt sometimes.
The module system/importing exporting (are they the same thing?). As I've been exploring other peoples libraries and code I'm starting so see a lot more things like (require prefix-in, or for-syntax rename-out), it makes things behave in ways I'm not used to and confuses me sometimes.
Language oriented Programming/macros. I've been using Racket on and off and I'm ashamed that I never have created a DSL, or written a macro for any of the code I write. For Macros my issues are...
I've never written a large enough program to feel like I've needed them
I don't have enough experience with them to go "Oh here is a good place to make a macro"
For LOP my issues are...
I don't have a PhD in programming language development, so how can I expect to come up with a good one or even know how to design one?
How do I know a DSL will make sense here?
Most people don't write day DSLs or languages as part of their standard coding (or at least I don't) so even if I recognize a situation or a new language, or DSL would make sense, it's almost always more complex than a beginner is able to make, or will take to my time to learn how to do properly, and so just gets skipped.
Debugging/Errors. I've run into this a lot more as I create larger programs. Racket doesn't give me the line number, or only vaguely points to the area where there is a problem. I end up having to run racket -l errortrace -t my-file.rkt because Racket doesn't add tracing by default. People say it's b/c it slows down the code, but I think that I would prefer that if all the debugging knobs were turned on by default, and that code could then be compiled in a release version with flags like 01/2/3 like in C/C++
Also I feel like the natural progression is to go from DrRacket->Emacs as you become more comfortable with Racket. I much prefer Emacs but DrRacket has better debugging tools and flipping back and forth in the middle of programming is an annoying context switch. I sometimes wish DrRacket was super performant so I could just use it for all of my Racket development.
Profiling. I have a prime_sieve implementation written in typed racket. I could do a whole write up on this experience, but I'll briefly talk about it here. Due to the nature of typed racket I was unaware that I needed to add types to the profiler on import to get it to not fail when I tried to profile functions. Also everything is in classes so how do I profile a class method? The whole documentation around here is confusing and there aren't many examples so I'm still trying to figure out this part
Optimization coach. Never has anything to say about my typed code. Could be using it wrong
Typed Racket. Not the silver bullet I thought it would be. Can make my program slower. Pain in the butt to interact with non-typed code. Some libraries don't have typed versions. For example I was using bit-vector in my non-typed prime_sieve implementation but transitioned to a typed vector b/c it didn't work in typed racket and I was too dumb to figure out why.
I could go on, but I've overcome a lot of the issues and stuck with it. Some people might be asking why go through all the suffering if you can just have a better experience in Python. Even with all my gripes I like Racket. I like that it's compiled, I like that it's faster than Python, I like that it gives me options to statically type my programs, I like that it runs on a VM like Java or .net, I like that it runs on all the OSes I use with minimal set up, I like Schemes, I like the pushes it's been making to be more accessible, whether that's by making more of its core written in Scheme, changing its license, creating a successor language that doesn't use parenthesis, etc. Also it has taught me a lot. It was faaaar easier to understand recursion in Racket than it was in Python, plus I use Python in my day job, so I'd rather be working in another language at home. That's all I will say about this for now, if you got this far thanks for reading, and I hope you are enjoying Racket as much as I am
I started in 1986 with Applesoft BASIC and learned Pascal, Assembler, Forth, Fortran, C++, Matlab, SQL, Perl, Python and Nim over the years (and a few other languages that I learned a little of). Several years ago I looked a bit into Haskell and became intrigued with Functional Programming, but it fell by the wayside somehow.
I haven't studied Computer Science; I have a PhD in Chemical Engineering. However, I changed careers and I've been a software developer for about 20 years now. I learned about "computer-sciency" stuff mostly from books.
Meanwhile I almost exclusively use Python for paid projects. I like Python a lot, but I dislike that it becomes more and more bloated and also wanted to use a different language in my free time for a change.
What surprised you when you came to Racket?
What was confusing and unfamiliar?
I combine the answers to these two questions because they seem to overlap. If something is confusing, it's probably surprising as well.
S-expressions and the use of parentheses surprised me, but I got used to them rather quickly I think. Now I really like this feature of the language. Somehow s-expressions have a certain "flow" to me, which I really enjoy.
I was pleasantly surprised that I could use more characters in identifiers than in most languages, a feature I always liked in Forth.
When I set out for a Python alternative, I was looking for a leaner language, but Racket seems to be the opposite. Nevertheless I still like Racket because it has other features I really like.
The values concept confused me somewhat because I find it conceptually easier to pass several values with multiple-item lists or tuples in other languages.
I actually was surprised that people were praising the documentation so much. Yes, it's very comprehensive, but I felt overwhelmed after having learned a few basics. Especially the Reference can be intimidating at first. Continuations? Syntax transformers? Match transformers? Ephemerons? Chaperones? What?
The problem aren't the concepts themselves (although some aren't trivial) but that I encountered them in contexts where they weren't relevant to me and only confused or distracted me. That's not to say that these concepts shouldn't be mentioned outside their dedicated chapters, but a glossary may help. As far as I can tell, there's no glossary for these terms in the documentation, is it?
If I hadn't had other experiences from learning other languages, I may have doubted myself and given up on learning Racket.
When I web-searched for more documentation, I usually found the same documentation set, only hosted at different universities. I rarely got hits for the Racket wiki, for example.
I miss better support for generics, especially after learning Nim as my first attempt to find an alternative to Python.
Although Racket "itself" is quite fast, I dislike the slow startup times, but fortunately they often don't matter that much. Still, I have in the back of my mind that I'll need to use a different language when startup time is important.
I really like the ability to create cross-platform binaries with raco cross.
Probably for historical reasons, there are (in my opinion) too many overlapping concepts. One example is sequences vs. streams vs. generators, which conceptually seem quite similar, especially the first two.
The Racket Slack has been very helpful and I enjoy the monthly meetings on Gather. I hope that this Discourse instance will replace or complement Slack to some extent because Discourse is so much better at retaining information.
I like the community. Thanks everyone for your help!
Which language(s) were you coming from? (if relevant)
I had a number of favorite languages, but the last one before Racket was Haskell. I wrote quite a bit of Haskell to support my research, and was quite fond of it. The main reason I switched was mainly because of the sexpr syntax and therefore syntax transformations. Also, I like writing bits of throwaway dirty prototype code from time to time, but Haskell wouldn't let me do that easily
What surprised you when you came to Racket?
Macros/syntax transformations are all over the place, but figuring out the really relevant use cases is not easy. (I'm still working on it )
Typed Racket is an wonderful project, but typing arbitrary Racket code is really hard, so there are many easy code fragments that don't typecheck. There have been a lot of exciting developments recently, though.
The module system is very expressive. I had never thought that modules could achieve this much. In particular, they are very easy to use to mix typed and untyped code.
Contracts are not types: contracts are more expressive, as they have access to the runtime values, but also trickier, since they can inadvertently alter the computation.
What was confusing and unfamiliar ?
Macros/syntax transformations: On paper, the principle is quite clear, but in practice there are a lot of details which may be hard to wrap your head around at first.
Typed Racket's typechecker is quite different from what I was used to in Haskell. It's no surprise, but may be very confusing at times. Both typecheckers may need hints sometimes, but in Typed Racket the hints are not the same. For example, the for/… forms may sometimes require an explicit type for the result.
Coming from Basic -> (Borland) Pascal/asm -> C -> Java -> Scala -> some Python and bash.
Confused as to where to start learning Racket. I have gone through books like The little Schemer (to learn Scheme) but Racket just looks like a totally different beast and am having trouble finding the appropriate "entry point" for me.
in addition to @Decabytes recommendations I'd like to recommend
a) the template repo 'cli-command' as a working example. (below)
b) using DrRacket, at least temporarily, as the highlighting/arrows, function signatures and direct access to the documentation help in getting used to Racket. If you are an Emacs user then Racket Mode and racket-xp-mode are also recommended
Before recommending any specific resources, I think I should ask: what do you want to do with Racket? It could be that you have a particular project in mind, or maybe you're interested in something that's different about Racket?
It was a long time ago so I don't remember much. But I do remember hating ... for a while because I didn't realize that ... within match is not the same as ... within define-syntax-rule. In my mind, ... had to be its own thing with its own self-contained meaning.
What if match was more consistent, such that when you have id ... in the match pattern you must also write id ... (instead of just id) in the match body? I think my beginner mind would have preferred that. But would that just shifted my confusion elsewhere? Because obviously a syntax pattern like (begin (define id 'id) ...) has no analogue in a match body. It's hard to say.
Thanks :). The problem is, I don't want to learn computer science from scratch (as much as I like reading books and studying stuff). To me, Racket seems a bit overwhelming (and I learned Scala so I think I earned the right to say this haha).
What I actually mean is that looking at "real" code, it is nothing like what the books and guides say/do. I do think this is not a Racket issue itself probably and it is a first world problem anyway so I am not really complaining, just providing my point of view.
p.s. Also, I feel like for some reason when I read Racket code, it "violates" my expectations of language purity for some reason. I can't put my finger on it, my apologies.
I've some experience with functional programming, mainly SML during my undergrad days. Professionally I've used a variety of languages, but no Lisps or functional languages.
Racket has been my first Lisp. I followed SICP using the sicp package. This helped in both learning the ropes of the language and following the book.
Coming to Racket, what I liked most was that the syntax made sense. Uniform s-expressions, internal defines, an aversion to mutation - it's all very clean, and works as expected. No fuss, no muss. This helped me greatly with organizing and writing my code. Importing and executing is also very convenient. Tail call optimzation is great; I could finally just write recursive functions. Parameters are also very useful once you get used to them.
Because the syntax is minimal, you already know how everything 'fits together'. From then on it's more about learning about all the features the language has to offer.
The Racket Guide is good enough to get you started. However, much of the functionality is only really explored in the Reference. Some time is needed to familiarize yourself with the Reference before it 'clicks' and becomes useful.
I also avoided iterations and comprehensions, in favour of the familiar recursion until I was comfortable enough with them to realize their convenience. Named lets were also new.
I only really understood quasiquote and unquote after coming back from learning Common Lisp. That's when I used learned what they were, and why Racket used syntax objects at all. Racket has already solved many problems I didn't even know I had. Many times I've felt that I'm playing catch-up with the language designers, but that's okay because there's always more great stuff to learn! On the other hand it did reinforce the importance of a historical perspective in pedagogy. To get why things are a certain way, it can very helpful to understand how they came to be.
I'm still learning about macros and continuations, but at this point I'm pretty comfortable with Racket as a whole. Fear of Macros is a great introduction. I'm looking forward to learning Scribble, building DSLs and using the #lang feature.
Edit: Thanks to @spdegabrielle for recommending racket-xp-mode! I was wondering what was missing from my Racket experience in Emacs...
Edit 2: Thanks to @greghendershott for Fear of Macros and Racket mode! Both have been immensely helpful.
@spdegabrielle Certainly. His Fear of Macros is also an excellent resource for all beginners in the community.
@maketo I would say that there are only those who haven't built DSLs yet. Racket is vast and has much to offer, and only after one knows their way around do they care to plumb its depths.
I can personally say that I didn't care much about DSLs until I got to know the language, and tinkered around a bit with syntax-parse. There's simply no analogue, no in, from other languages' experiences.
I would say the people who don't build DSLs would be far more than those that do - many would be students or beginners. Vanilla Racket has more than enough tools to tackle most problems imo, and a macro here and there suffices. I would suggest a conservative estimate of 80:20, though I don't have any data to back this up.
Yeah, this is what helped me stick with Racket the longest out of all the languages I've tried to learn. Not having to keep my nose stuck in a syntax reference really helped with figuring out how to glue together the various simple parts, something that I'd always struggled with in languages like Python.
Coming from Python. I was curious about LISP and attracted by the generally good reputation of Racket - good docs, runtime etc. Seems to have plenty of libraries.
I tried it out on a couple of toy projects.
The number one frustration I ran into was having to deal with "other people's macros".
I had assumed macros were I thing I could just ignore until I wanted to write my own.
But many of the libraries you're going to use (I remember this particularly with db and rackunit) have interfaces built out of macros.
As a user of the library, you encounter something that looks like a function that takes a string argument. But... surprise! ...it's a macro. Suddenly you can't substitute "an expression that returns a string" in place of the string arg, because it's a macro, so it works at the syntax level rather than the value level, i.e. your expression hasn't been evaluated yet.
But expressions using functions and macros both look the same in your source code, and the lib docs don't tend to point out "this is a macro" and the attendant restrictions, so you only find out when you try and use it and it doesn't work.
So the surprising thing was realising that my prior idea that Racket would be a language "with s-expression syntax (i.e. prefix notation) and macros" was naive. And that really s-expressions are closer to a complete absence of syntax, and everything is built out of macros.
A confusing/unfamiliar thing was - exactly what is the execution model? As a mental model I was thinking of macros running "at compile time" and everything else at "run time". But I think it's not 100% accurate and things are potentially more dynamic than that, but I didn't get as far as grokking it.
Another surprising thing was contracts.
AFAICT it provides always-on, run-time type-checking. It seems to be used pretty ubiquitously throughout the Racket ecosystem, so I guess it can't be bad. The flexibility it provides for defining constraints is great.
But... the docs suggest that where performance is a concern, the library author should provide a separate version of the library without contracts. This seemed quite back-to-front to me. Firstly, that requirement is cumbersome enough that it seems most libraries don't bother. Second, it feels like something I ought to be able to configure as a user of any library, without needing any special provisions from the author. i.e. I should be able to run my test suite with contracts on, or export a production build with them off. (Or maybe that's possible and I just didn't see it!)
Overall I was pretty happy with the Racket experience, but also felt I'd probably only come back to it if I really wanted to do DSL/language programming specifically.
I had assumed macros were I thing I could just ignore until I wanted to write my own.
But many of the libraries you're going to use (I remember this particularly with db and rackunit ) have interfaces built out of macros.
As a user of the library, you encounter something that looks like a function that takes a string argument. But... surprise! ...it's a macro. Suddenly you can't substitute "an expression that returns a string" in place of the string arg, because it's a macro, so it works at the syntax level rather than the value level, i.e. your expression hasn't been evaluated yet.
Do you have any examples you can point to?
I agree that having macros means that libraries must take additional care in their documentation, but we do try to take that care, at least to some degree. For example, the blue box for a macro is labeled "syntax" vs "procedure" for a procedure. It would be useful to know where we fell short for you.
Thanks for your comments. (And the same to the other responders, too!)