Static languages, in particular Haskell, PureScript, etc, are
unparalleled when it comes to fast prototyping or design space exploration. The compiler can show you whether your idea even makes sense before writing big chunks of business code.
How? By building the structure/hard stuff first and letting the compiler verify it while leaving out the uninteresting parts.
A very simple example. Let's say you have some data model with people, addresses, whatever. You want to see what you need to do to get a list of addresses for some group of people.
You start by defining the structure of your data model (this can depend on DB schema, business requirements, whatever):
data Person = Person { name :: String, city :: String }
data Address = Address { city :: String, zipcode :: String, street :: String }
Now, go straight to the hard part. No need to fuck around with DB connections, mock data, whatever:
getAddresses :: [Person] -> [Address]
getAddresses = undefined
Leave out the implementation for now. So probably we are gonna need a way to get an Address for a Person. Ok:
addressForPerson :: Person -> Address
addressForPerson = undefined
Again, leave out the implementation. Now go back to `getAddresses` and see if we can implement it now:
getAddresses = map addressForPerson
Cool, the compiler doesn't complain. So far we've verified that
if we have a function like `addressForPerson` we can safely assume that our design is going to work.
Now we can go back to `addressForPerson` and implement that. But turns out, we only have a city stored in Person, so the best we can do is get back a list of potential addresses for each person. So we need to adjust that type signature:
addressForPerson :: Person -> [Address]
addressForPerson = undefined
Leave out implementation, it's trivial so no need to spend time on it now. But now the compiler complains that `getAddresses` doesn't make sense anymore! We are saying that, having a list of people, we can get a list of addresses, but that's not true anymore. We found out that we can only get a list of potential addresses, so we have to adjust the type signature.
getAddresses :: [Person] -> [[Address]]
Now every use of `getAddresses` will have to be adjusted to the new requirements. Etc etc.
Granted, this is a very simple example, but I hope it illustrates how static languages facilitate extremely fast prototyping. Sure, a Lisp REPL is very very nice, but in my personal experience it's no match for a type system. We didn't even have to mock data to start experimenting!
(And yes, Haskell, OCaml, PureScript, Elm etc all have a REPL too).