TypesHaskell being statically typed will outlaw a large-ish number of direct lispisms until you provide the compiler sufficient type-based justification that the operation is OK. For instance, new lisp->haskell programmers often want to make arbitrary nested lists
[[1,2,3],4,[[5,6],7]]
which is not well-typed so the compiler complains. Honestly, what you need is a different type than the strict Haskell list called a Rose Tree
data Rose a = Leaf a | Branch [Rose a]
which tells the compiler that you explicitly want arbitrary nesting.
---
Laziness
All of Haskell has default laziness and this means that your environment is fully tilted toward laziness. Generally this means that a thing called equational reasoning holds nicely and this forms a MAJOR component of your ability to reason about Haskell code. In particular, given any repeated substatement, you can "lift" it up with a let
... e ... e ... e ...
==
let x = e
in ... x ... x ... x ...
Lazy Racket gives this to you too, but having it
everywhere is a new thing. You also are able to rely on control structures as combinators much more so that something like
fold x c . map f . map g
is very common in Haskell since laziness will automatically fuse each step, while in (strict) Racket you'd want to manually fuse them together
fold (c . f . g) x
reducing composability.
---
Purity
You can get pretty close to pure in Racket, but Haskell takes this concept much further. Purity and laziness are a driving force which leads to the need for things like Monads... and strong types allow the boundaries of various semantic segments to be very sharp. As an example, the STM libraries in Haskell are remarkably nice due to a combination of strong types and purity. In particular, you tend to build up computations of types like
STM a, STM b, STM (a -> b -> c)
where the STM marks that these computations only make use of transactionally safe memory and are allowed to be re-run as many times as needed to ensure linearity. Then you use
atomically :: STM a -> IO a
which "upgrades" STM to IO allowing now the entire set of side-effects by interpreting the STM computation as IO... and thus choosing to run and re-run it until it linearizes.
---
In each of these cases, Racket, being the flexible language it is, has methods of nicely including the feature—typed racket, lazy racket, base racket without ambient state or io—but Haskell goes fully and confidently into these three choices and that subsequently leads to very interesting combination effects and a community and ecosystem designed in a particular, interesting style.
In other words, I believe that even if you're intimately familiar with each of those things in isolation, Haskell may be the first time you've ever seen them together... and that changes everything.