wrangling the internet of things with haskell production haskell Reid Draper @reiddraper
production haskell Reid Draper @reiddraper
production haskell today
production haskell at helium 12 months in
lowest defect rate
lowest defect rate easiest to refactor
lowest defect rate easiest to refactor most enjoyable OSS libraries for everything*
20k LOC
20k LOC 5 developers
20k LOC 5 developers 6 production services
build deploy monitor test develop
build deploy monitor test develop
cabal hell
cabal hell
stack haskellstack.org
ghc packages snapshot project specific
20k LOC 2 seconds
ordeal github.com/reiddraper/ordeal
stack build self-contained executable
build deploy monitor test develop
app ��� assets � ��� images � � ��� favicon.ico � ��� javascript � � ��� site.js � ��� stylesheet � ��� site.css ��� bin ��� app
tar -xzf app-370b347.tgz ln -s app-370b347 current restart app
ln -s app-83181ad current restart app
60MB
build deploy monitor test develop
ekg
stdout file syslog
build deploy monitor test develop
QuickCheck
propEvent :: Event -> Bool propEvent a = Just a == decode (encode a)
Event roundtrips in JSON: FAIL (0.04s) *** Failed! Falsifiable (after 1 test): Event {_severity = Error, _facility = Unspecified, _sampled = 1894-02-22 00:00:04.5986 UTC, _eventDetails = ElementDisconnect {_elementMAC = 8ec52584b2c82424}, _eventMessage = Nothing, _originator = http://www.ietf.org/rfc/rfc2396.txt, _reference = Just http://www.ietf.org/rfc/rfc2396.txt }
Event roundtrips in JSON: FAIL (0.04s) *** Failed! Falsifiable (after 1 test): Event {_severity = Error, _facility = Unspecified, _sampled = 1894-02-22 00:00:04.5986 UTC, _eventDetails = ElementDisconnect {_elementMAC = 8ec52584b2c82424}, _eventMessage = Nothing, _originator = http://www.ietf.org/rfc/rfc2396.txt, _reference = Just http://www.ietf.org/rfc/rfc2396.txt }
ghci> t 1894-02-22 00:00:04.5986 UTC ghci> Data.Aeson.encode t "\"1894-02-22T00:00:04.598Z\""
build deploy monitor test develop
postgresql-transactional github.com/helium/postgresql-transactional
lookupUser emailAddress = queryFirst [emailAddress] [sql| SELECT id, email, name, timezone FROM user_account WHERE email = ? |]
foo
foo bar
foo bar baz
quz foo bar baz
quz foo bar baz buz
fiz quz foo bar baz buz
BEGIN BEGIN COMMIT COMMIT
BEGIN BEGIN COMMIT COMMIT
BEGIN BEGIN COMMIT COMMIT
WARNING: there is already a transaction in progress WARNING: there is no transaction in progress
fiz quz foo bar baz buz
fiz quz foo bar baz buz
quz foo bar baz buz
lookupUser :: Connection -> Text -> IO (Maybe User) lookupUser connection emailAddress = queryFirst [emailAddress] [sql| SELECT id, email, name, timezone FROM user_account WHERE email = ? |] connection
createUser :: Connection -> NewUser -> IO User createOrganization :: Connection -> NewOrganization -> IO Organization addUserToOrganization :: Connection -> User -> Organization -> IO ()
createUserAndOrganization :: Connection -> NewUser -> IO () createUserAndOrganization connection newUser = withTransaction connection $ do let newOrganization = NewOrganization (newUser ^. name) user <- createUser connection newUser org <- createOrganization connection newOrganization addUserToOrganization connection user org
-- NOTE: you MUST wrap this in a transaction!! createUserAndOrganization :: Connection -> NewUser -> IO () createUserAndOrganization connection newUser = do let newOrganization = NewOrganization (newUser ^. name) user <- createUser connection newUser org <- createOrganization connection newOrganization addUserToOrganization connection user org
withTransaction conn $ do createUserAndOrganization conn newUser somethingElse conn
types to the rescue
lookupUser :: Connection -> Text -> IO (Maybe User) lookupUser :: Text -> PGTransaction (Maybe User)
runPGTransaction :: PGTransaction a -> Postgres.Connection -> IO a
createUserAndOrganization :: NewUser -> PGTransaction () createUserAndOrganization newUser = let newOrganization = NewOrganization (newUser ^. name) user <- createUser newUser org <- createOrganization newOrganization addUserToOrganization user org foo = do createUserAndOrganization newUser somethingElse runPGTransaction foo connection
notAllowed = do user <- createUser newUser someSlowSideEffectingThing return user
notAllowed = do user <- createUser newUser someSlowSideEffectingThing :: IO () return user
postgresql-transactional github.com/helium/postgresql-transactional
build deploy monitor test develop
Reid Draper @reiddraper github.com/helium helium.com
Recommend
More recommend