How to Sell Excellence
Michael O. Church
Chicago Haskell Meetup
March 19, 2015
“I iz standing on the shoulders of giants!”
Is this a business talk or a Haskell talk?
Well, it’s both…
Haskell is an excellent language for excellent programmers, but, because it’s unfamiliar, can be hard to sell to “The Business”.
You know, this guy.
You know, this guy.
Ok, that’s not quite fair, and we’ll talk about that.
What to expect
This is a talk about Haskell and selling Haskell.
In general, excellence is hard to sell.
This problem isn’t limited to Haskell, though we’ll focus on it, because…
In general, excellence is hard to sell.
This problem isn’t limited to Haskell, though we’ll focus on it, because…
In general, excellence is hard to sell.
This problem isn’t limited to Haskell, though we’ll focus on it, because…
In general, excellence is hard to sell.
This problem isn’t limited to Haskell, though we’ll focus on it, because…
Me at the wrong time of day
Me Anyone at the wrong time of day
An aside...
Don’t call yourself (or another programmer) a “bozo” when talking to business types. We know that even if we’re really good at our jobs, we make mistakes.
We self-deprecate because we’re used to being corrected by our subordinates (computers). “Fix or no compile.”
An aside...
Don’t call yourself (or another programmer) a “bozo” when talking to business types. We know that even if we’re really good at our jobs, we make mistakes.
We self-deprecate because we’re used to being corrected by our subordinates (computers). “Fix or no compile.”
Executives aren’t. They’re constantly told that they’re awesome. Cultural differences, ya know.
An aside...
Don’t call yourself (or another programmer) a “bozo” when talking to business types. We know that even if we’re really good at our jobs, we make mistakes.
We self-deprecate because we’re used to being corrected by our subordinates (computers). “Fix or no compile.”
Executives aren’t. They’re constantly told that they’re awesome. Cultural differences, ya know.
Nix self-deprecation, adopt their baseless smooth, calming confidence.
(a little self-deprecation goes a long way)
Actually, I’m more of a curmudgeon
Actually, I’m more of a curmudgeon
Actually, I’m more of a curmudgeon
I think like this…
Actually, I’m more of a curmudgeon
Nail myNail = null; //momentary idiocy
Shoe myShoe = new Shoe (myNail);
...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hours later (in runtime):
�KingdomLostException
at KingdomVisitorFactory.java:9784
...
Caused by: NullPointerException
HORRIBLE error/failure distance!
Error / failure distance
If the logical fault (error) is far away from the manifestation (failure) then you are in for a looooong debug session.
Lisp’s nil overloading adds conceptual beauty but enables gaping error/failure distances.
Actually, I’m more of a curmudgeon
I think like this…
… so I can’t stand bad code!
Anyway, static typing is very good.
Some argue that...
Some argue that static typing mainly catches “stupid” bugs.
Most bugs are stupid. (Find one!)
(get :is-pubic {:is-public true :value 23})
=> nil
Some argue that...
Some argue that static typing mainly catches “stupid” bugs.
Most bugs are stupid. (Find one!)
(get :is-public {:is-public true :value 23})
=> nil
Some argue that...
Some argue that static typing mainly catches “stupid” bugs.
Most bugs are stupid. (Find one two!)
(get :is-public {:is-public true :value 23})
=> true
… but “Haskell is weird”!!
In truth… it’s not that weird.
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
?
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Anyway, let’s talk FP...
Modern languages provide the option of functional programming. (Yay?)
Even Java has lambdas as of 1.8.
Unfortunately, while...
Modern languages provide the option of functional programming.
THAT’S NOT NEARLY ENOUGH!
Code quality is determined by what people do, not what the language enables.
To know good vs. bad Scala style...
Functional programming is the way forward
Bad news: to reason about arbitrary code is mathematically impossible (Turing’s Halting Problem, Rice’s Theorem).
Functional programming is the way forward
Bad news: to reason about arbitrary code is mathematically impossible (Turing’s Halting Problem, Rice’s Theorem).
Good news: programmers don’t write “arbitrary code”. Their programs exist to solve specific problems, in a low-entropy subspace of “all code”.
Functional programming is the way forward
Bad news: to reason about arbitrary code is mathematically impossible (Turing’s Halting Problem, Rice’s Theorem).
Good news: programmers don’t write “arbitrary code”. Their programs exist to solve specific problems, in a low-entropy subspace of “all code”.
Style Guide: “Don’t write arbitrary code!”
Me Anyone at the wrong time of day
FP is the way forward (2)
Sadly, programs often evolve into high-entropy “arbitrary code”.
No silver bullet, but simple “building blocks” that compose well are a start.
FP is the way forward (3)
OOP has 23 design patterns and most of them suck.
FP has two design patterns that compose very well.
Even this separation’s artificial: “verbs” double as “nouns”.
FP is the way forward (4)
Functional programming values reproducibility and favors explicit (“threaded”) state over the implicit kind.
Global state has its negatives, but promiscuously strewn local state (OOP) is often just as bad.
In functional programming, mutability is allowed as an optimization but not the default.
FP is the way forward (5)
-- DON'T DO THIS!! (implicit state)
ctr :: IORef Int
ctr = unsafePerformIO $ newIORef 1
incr :: IO Int
incr = do
x <- readIORef ctr
writeIORef ctr $ x + 1
return x
data Counter = C {get :: Int}
incrExpl :: Counter -> (Int, Counter)
incrExpl c =
let x = get c in
(x, C (x + 1))
-- Explicit state is:
-- * thread/revision safe.
-- * configuration-agnostic.
-- * reproducible.
-- * easier to reason about.
FP is the way forward (6)
Haskell does allow state and mutability with specialized nouns: reference cells like IORef and TVar, and composable stateful actions (IO a, STM a).
getChar :: IO Char -- “IO-capable, returns a Char”
putChar :: Char -> IO ()
λ let echo = do { c <- getChar; putChar c }
echo :: IO ()
FP is the way forward (7)
Haskell doesn’t eliminate state or “side effects”.
It just gives us the tools to manage them, in the type system.
Haskell is possibly the best imperative language.
We suck at selling functional programming (FP)
We suck at selling functional programming (FP)
It’s not actually stateless programming, and selling a negative (“doesn’t have state”) is a losing strategy.
It’s state-aware programming.
In Haskell, compile-time types can tell you:
The next ~10 minutes assume Haskell exposure.
If you’re familiar with Haskell, I’ll probably be annoyingly slow.
If you’re not, I’ll be moving fast.
… and then we’ll get back to business.
Let’s review some Haskell types.
Let’s review some Haskell types.
| JNum Double | JStr String | JBool Bool
| JNull
Let’s review some Haskell types.
Let’s review some Haskell types.
A Haskell executable has an action called main of type IO () that is executed.
Haskell provides no “sane” way to turn an IO into a “pure” value, e.g. IO Int into Int. Thus, if a computation is “in IO”, enclosing computations will return IO types.
Let’s review some Haskell types
The IO type is an instance (but not the only one) of the typeclass Monad. A monad is an abstraction that allows us to encode a notion of computational context into our type system.
IO is the ultimately impure monad. IO a means “I can have stateful effects, but I produce an a”.
Some example IO.
getChar :: IO Char
putChar :: Char -> IO ()
getCurrentTime :: IO UTCTime
listDirectory :: FilePath -> IO [FilePath]
catch :: IO a -> (IOError -> IO a) -> IO a
randomIO :: IO Int --actually (Random a) => IO a
return :: a -> IO a --actually more general
bind :: IO a -> (a -> IO b) -> IO b --ditto
I will try to sell Monad.
Craps is a “stateful” game. You roll two dice.
Find two instances of state in this game.
I will try to sell Monad.
Craps is a “stateful” game. You roll two dice.
Haskell can be stateful...
import System.Random
roll :: IO Int
roll = do
x <- randomRIO (1, 6)
y <- randomRIO (1, 6)
return (x + y)
youWin :: IO ()
youWin = putStrLn "You won!"
youLose :: IO ()
youLose = putStrLn "You lose!"
main :: IO ()
�main = do
point <- roll
case point of
p | p `elem` [2, 3, 12] -> youLose
p | p `elem` [7, 11] -> youWin
_ -> loop point
where
loop point = do
r <- roll
if r == point then youWin
else if r == 7 then youLose
else loop point
Haskell can be stateful...
import System.Random
roll :: IO Int
roll = do
x <- randomRIO (1, 6)
y <- randomRIO (1, 6)
return (x + y)
youWin :: IO ()
youWin = putStrLn "You won!"
youLose :: IO ()
youLose = putStrLn "You lose!"
main :: IO ()
�main = do
point <- roll
case point of
p | p `elem` [2, 3, 12] -> youLose
p | p `elem` [7, 11] -> youWin
_ -> loop point
where
loop point = do
r <- roll
if r == point then youWin
else if r == 7 then youLose
else loop point
explicit (threaded) state
implicit state
Haskell can be stateful...
import System.Random
roll :: IO Int
roll = do
x <- randomRIO (1, 6)
y <- randomRIO (1, 6)
return (x + y)
youWin :: IO ()
youWin = putStrLn "You won!"
youLose :: IO ()
youLose = putStrLn "You lose!"
… but we don’t need to do our dice rolling in the IO monad!
We have no guarantee that roll isn’t wiping the DB.
Haskell can be stateful...
… but we don’t need to do our dice rolling in the IO monad!
We have no guarantee that roll isn’t wiping the DB.
That sounds like a ridiculous thing to fear…
...unless you’ve ever worked on a large multi-engineer program.
We don’t need “IO” for the dice.
We were able to use explicit state for the player’s “point” and can do the same for the random number generator.
(State s) a = s -> (a, s) --(in essence)
A “State s a” is a deterministic action (on state of type s) that returns an a. The state is an explicit parameter.
An “IO a” is an IO-capable action that returns an a when executed. State is an implicit parameter.
Replacing IO with (State StdGen)
import System.Random
import Control.Monad.State
d6 :: State StdGen Int
d6 = state $ randomR (1, 6)
roll :: State StdGen Int
roll = do
x <- d6
y <- d6
return $ x + y
data Result = Win | Lose deriving Show
play :: State StdGen Result
play = do
point <- roll
case point of
p | p `elem` [2, 3, 12] -> return Lose
p | p `elem` [7, 11] -> return Win
_ -> loop point
where loop point = do
r <- roll
if r == point then return Win
else if r == 7 then return Lose
else loop point
main :: IO ()
main = do {gen <- newStdGen;
print $ evalState play gen}
The dreaded “M word” (1)
State s a (or, equivalently, (State s) a) has an apparent similarity to IO a.
The dreaded “M word” (2)
State s a has an apparent similarity to IO a. This similarity facilitates code reuse.
roll :: IO Int
roll = do
x <- randomRIO (1, 6)
y <- randomRIO (1, 6)
return (x + y)
roll :: State StdGen Int
roll = do
x <- d6
y <- d6
return (x + y)
The dreaded “M word” (3)
Monad allows us to abstract over “computational context”.
The dreaded “M word” (4)
This allows for code reuse, because we’ve abstracted over computational context.
rollDieIO :: IO Int
rollDieIO = randomRIO (1, 6)
rollDieState :: State StdGen Int
rollDieState = state $ randomR (1, 6)
sequence :: (Monad m) => [m a] -> m [a] -- e.g. [IO a] => IO [a]
rollDice :: (Monad m) => Int -> m Int -> m [Int]
rollDice n d6 =
sequence (take n (repeat d6))
The dreaded “M word” (5)
Haskell’s radicalism about type discipline and stateful effects is actually about the creation of high-quality, simple components that one can reason about-- even if they were written years ago, or by another author.
The dreaded “M word” (5)
Haskell’s radicalism about type discipline and stateful effects is actually about the creation of high-quality, simple components that one can reason about-- even if they were written years ago, or by another author.
A consequence of forcing state to be included in type signatures (e.g. IO) is that it encourages people to write smaller, more modular functions that avoid state unless they need it.
That’s the pitch (for Monad).
When what you’re selling is of high quality, it’s better to teach (as much as you can) than to sell…
… of course, to fully teach Monad would require another talk or two.
Enough about Monads. Feed us!
Selling Haskell to programmers
Selling Haskell to programmers (2)
Selling Haskell to programmers (3)
Actually… debugging sucks, testing sucks somewhat less, and compiling (+ QuickChecking) sucks a whole lot less.
Selling Haskell to programmers (4)
Selling Haskell to programmers (4)
Selling Haskell to programmers (4)
Selling Haskell to programmers (5)
Haskell isn’t the only functional programming language.
There’s also Erlang, Common Lisp, Clojure, OCaml, F#, Scala, Swift, Scheme...
Selling Haskell to programmers (5)
Haskell isn’t the only functional programming language.
There’s also Erlang, Common Lisp, Clojure, OCaml, F#, Scala, Swift, Scheme...
… but Haskell is the only major language whose type system can verify the lack of side effects.
Selling Haskell to programmers (5)
Haskell isn’t the only functional programming language.
There’s also Erlang, Common Lisp, Clojure, OCaml, F#, Scala, Swift, Scheme...
… but Haskell is the only major language whose type system can verify the lack of side effects.
factorial :: Int -> Int does not wipe the DB.
Selling Haskell to programmers (6)
Static typing isn’t nearly as restrictive as some fear.
�It’s easier to get the dynamism you want in a static language (see: Write Yourself a Scheme in 48 Hours) than to exclude behaviors that you don’t want in a dynamic one.
Selling Haskell to programmers (6)
Static typing isn’t nearly as restrictive as some fear.
�It’s easier to get the dynamism you want in a static language (see: Write Yourself a Scheme in 48 Hours) than to exclude behaviors that you don’t want in a dynamic one.��There are great programs written in dynlangs, but 99% of programs that “require dynamic typing” are badly designed.
Selling Haskell to programmers (7)
Haskell is fast as hell.
Am I talking about executable performance or the speed of development?
Selling Haskell to programmers (7)
Haskell is fast as hell.
Am I talking about executable performance or the speed of development?
Both! Haskell’s static typing doesn’t slow development by much, and reduced debugging time is huge.
Selling Haskell to programmers (7)
Haskell is fast as hell.
Am I talking about executable performance or the speed of development?
Both! Haskell’s static typing doesn’t slow development by much, and reduced debugging time is huge.
(Okay, full disclosure. Haskell takes some time to learn. After that, it’s fast.)
Productivity
Time invested
2 days
2 wks
3 mth.
1 yr.
5 yrs.
Haskell
“Brand X”
Enterprise Java/C#
Selling Haskell to the Business
Understanding the tribes
Great programmers want to build great systems that run without maintenance, so they can move on to better things.
Bad business people “cut costs” (externalize) and play politics. They can’t grow the pie, so they fight over slices.
Understanding the tribes
Great programmers want to build great systems that run without maintenance, so they can move on to better things.
Bad programmers create incomprehensible systems that only they can maintain, in order to protect their jobs.
Bad business people “cut costs” (externalize) and play politics. They can’t grow the pie, so they fight over slices.
Understanding the tribes
Great programmers want to build great systems that run without maintenance, so they can move on to better things.
Bad programmers create incomprehensible systems that only they can maintain, in order to protect their jobs.
Great business people want excellent processes and companies, in order to generate new wealth.
Bad business people “cut costs” (externalize) and play politics. They can’t grow the pie, so they fight over slices.
Understanding the tribes
Great programmers want to build great systems that run without maintenance, so they can move on to better things.
Bad programmers create incomprehensible systems that only they can maintain, in order to protect their jobs.
Great business people want excellent processes and companies, in order to generate new wealth.
Bad business people “cut costs” (externalize) and play politics. They can’t grow the pie, so they fight over slices.
Sell to these guys (and gals)!
Selling (anything) to the business
Programmers tend to have a tribal condescension toward “the Business”. Drop that. “They” are often as smart as we are, just with different specialities.
I used to think that it was “idiot businessmen” who were responsible for the awfulness of the software industry.
Selling (anything) to the business
Programmers tend to have a tribal condescension toward “the Business”. Drop that. “They” are often as smart as we are, just with different specialities.
I used to think that it was “idiot businessmen” who were responsible for the awfulness of the software industry.
In fact, it’s (often, smart) businessmen listening to idiots.
Selling (anything) to the business (2)
I used to think that it was “idiot businessmen” who were responsible for the awfulness of the software industry.
In fact, it’s (often, smart) businessmen listening to idiots.
Yes, this is good news. It means that there’s room for empathy. We can figure out why this is.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Where “story points” and stack ranking (false precision) come from.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (3)
Why do smart business people often listen to idiots about technology? Cultural differences.
Selling (anything) to the business (4)
Selling (anything) to the business (5)
It shouldn’t be that hard! If people can make millions selling crap...
Sand Hill Road gave this guy $25 million in seed funding to “scale up” fecal production.
Selling (anything) to the business (6)
It shouldn’t be that hard! If people can make millions selling crap…
… then we should be able to do OK selling excellence. A good product is an advantage.
Focus on shared wants
Haskell makes code extremely fast, robust and secure, while remaining highly productive for programmers. Its type system allows construction of very reliable programs, to an extent nearly impossible to meet in existing languages (e.g. Java, C++). Its compiler is world-class and its programmers are excellent.
Focus on shared wants
Haskell makes code extremely fast, robust and secure, while remaining highly productive for programmers. Its type system allows construction of very reliable programs, to an extent nearly impossible to meet in existing languages (e.g. Java, C++). Its compiler is world-class and its programmers are excellent.
Focus on shared wants (2)
As important as the merit of a language is the quality of the average programmer.
Yes, there are great Java programmers.
Focus on shared wants (2)
As important as the merit of a language is the quality of the average programmer.
Yes, there are great Java programmers. It’s hard as hell to find them.
Focus on shared wants (2)
As important as the merit of a language is the quality of the average programmer.
Yes, there are great Java programmers. It’s hard as hell to find them. Oh, and they’re incredibly expensive.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
Haskell engineers seem more expensive.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
Haskell engineers seem more expensive… UNLESS you compare apples to apples
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
25%
50%
75%
90%
99%
99.9%
$
$$
$$+
$$$
$$$$
$$$$
25%
50%
75%
90%
99%
99.9%
$$
$$+
$$$
$$$+
$$$$
$$$$
Java engineers: price by percentile ability.
Haskell engineers: price by percentile ability.
In case it’s not clear...
The Haskell engineer is a bargain, relative to a comparable Java engineer.
Establish positional equality.
If you position yourself as a subordinate, no one will listen to you.
I’ve seen engineers sink their own initiatives with the “...but it was strictly a weekend project” retreat.
Establish positional equality.
If you position yourself as a subordinate, no one will listen to you.
I’ve seen engineers sink their own initiatives with the “...but it was strictly a weekend project” retreat.
To a businessman skilled at negotiation, that suggests that the project was not important.
Establish positional equality. (2)
If you position yourself as a dominant, no one will trust or like you.
Non-technical people are bad at evaluating technical work and people, but that doesn’t make you smarter.
Establish positional equality. (2)
If you position yourself as a dominant, no one will trust or like you.
Non-technical people are bad at evaluating technical work and people, but that doesn’t make you smarter.
It makes you politically weak against charlatans with better social skills.
Establish positional equality. (3)
Why is there so much mediocrity (SCRUM, enterprise Java) in software?
Establish positional equality. (3)
Why is there so much mediocrity (SCRUM, enterprise Java) in software?
Answer: It’s non-threatening (if expensive) to have 100 mediocre engineers instead of 5 good ones.
Establish positional equality. (3)
Why is there so much mediocrity (SCRUM, enterprise Java) in software?
Answer: It’s non-threatening (if expensive) to have 100 mediocre engineers instead of 5 good ones.
So avoid the tribal “I’m smarter than you” air. Business people aren’t idiots; many trust idiots, but you’ve probably trusted an idiot or two in your life, too.
Establish positional equality. (4)
Represent yourself as an equal, working together toward a shared goal. For Haskell, this could be:
These are all things that progressive business people want!
Teach!
You’re not just a programmer or an elite problem-solver. If you’re worth your salt, you’re also a teacher.
It’s not enough to build the asset, commit it to version control, and write a few unit tests. Teach people how to use it.
Teach! (2)
If you want to convince someone of something (such as the value of your software) you cannot expect them to do the hard work of convincing themselves.
There is no shortcut. You have to do the legwork and make your case obvious, and the material accessible.
This also means...
(sorry)
The future...
Author: Brian Kammerzelt (http://clayimaging.smugmug.com/)
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history
1950s-1980s: MIT / Bell Labs Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
Some technical history (2)
1980s-2010s: Silicon Valley Culture.
What’s possible?
What’s possible?
What’s possible?
What’s possible?
… and Haskell is (a) language to do it in.
… and Haskell is (a) language to do it in.
The idea that languages are suited only for rapid prototyping (sloppy, fast to write) or production (ugly, slow to write) is a false dichotomy. Haskell excels at both.
… and Haskell is (a) language to do it in.
Write performant, robust, excellent, trustable code…
… and Haskell is (a) language to do it in.
Write performant, robust, excellent, trustable code…
… and quickly, too!
Questions?