Real World Haskell - Cassandra Part 1 - Connecting to Cassandra
I use Cassandra at work and was wondering what a complete solution for connecting to it with Haskell would look like. The following will function mainly as a cookbook for connecting to Cassandra but is also littered with the real world sharp edges (and their workarounds) you sometimes have to deal with in Real World Haskell development.
I go to Hackage and type in “Cassandra” and these results are returned:
cassandra-thrift library
thrift bindings to the cassandra database
library Last uploaded on Apr 21, 2013
cassandra-cql library
Haskell client for Cassandra's CQL protocol
bsd3, library Last uploaded on Jul 30, 2015
cql-io library
Cassandra CQL client.
library, mpl Last uploaded on Jun 17
hscassandra library
cassandra database interface
bsd3, library Last uploaded on Mar 17, 2011
cql library
Cassandra CQL binary protocol.
library Last uploaded on Jun 17
cassy library
A high level driver for the Cassandra datastore
bsd3, library Last uploaded on Sep 2, 2014
Quelea library
Programming with Eventual Consistency over Cassandra.
bsd3, library Last uploaded on Dec 13
Thrift library
Haskell bindings for the Apache Thrift RPC system
library Last uploaded on Apr 12, 2013
I don’t particularly want the cassandra-thrift library bindings, so I’ll try out the cassandra-cql library.
/tmp λ stack new test-cassandra-cql simple
Downloading template "simple" to create project "test-cassandra-cql" in test-cassandra-cql/ ...
The following parameters were needed by the template but not provided: author-email, author-name, category, copyright, github-username
You can provide them in /home/cody/.stack/config.yaml, like this:
templates:
params:
author-email: value
author-name: value
category: value
copyright: value
github-username: value
Or you can pass each one as parameters like this:
stack new test-cassandra-cql simple -p "author-email:value" -p "author-name:value" -p "category:value" -p "copyright:value" -p "github-username:value"
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- test-cassandra-cql/test-cassandra-cql.cabal
Selecting the best among 8 snapshots...
* Matches lts-6.9
Selected resolver: lts-6.9
Initialising configuration using resolver: lts-6.9
Total number of user packages considered: 1
Writing configuration to file: test-cassandra-cql/stack.yaml
All done.
Modify the test-cassandra-cql.cabal file to look like this:
name: test-cassandra-cql
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/test-cassandra-cql#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable test-cassandra-cql
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
cassandra-cql
Stackage doesn’t yet have cassandra-cql so it’ll fail when we try to build. I’ll run stack solver --update-config
followed by stack build
first. I also didn’t yet have cabal-install installed so I’ll add on stack install cabal-install
:
/tmp/test-cassandra-cql λ stack install cabal-install && stack solver --update-config && stack build
Cabal-1.22.8.0: download
Cabal-1.22.8.0: configure
Cabal-1.22.8.0: build
Cabal-1.22.8.0: copy/register
cabal-install-1.22.9.0: download
cabal-install-1.22.9.0: configure
cabal-install-1.22.9.0: build
cabal-install-1.22.9.0: copy/register
Completed 2 action(s).
Copying from /home/cody/.stack/snapshots/x86_64-linux/lts-6.9/7.10.3/bin/cabal to /home/cody/.local/bin/cabal
Copied executables to /home/cody/.local/bin:
- cabal
Using configuration file: stack.yaml
Using cabal packages:
- test-cassandra-cql.cabal
Using resolver: lts-6.9
Using compiler: ghc-7.10.3
Asking cabal to calculate a build plan...
Trying with packages from lts-6.9 as hard constraints...
Successfully determined a build plan with 1 external dependencies.
The following changes will be made to stack.yaml:
* Resolver is lts-6.9
* Dependencies to be added
extra-deps:
- cassandra-cql-0.5.0.2
Updated stack.yaml
hslogger-1.2.10: using precompiled package
Decimal-0.4.2: using precompiled package
network-info-0.2.0.8: using precompiled package
resource-pool-0.2.3.2: using precompiled package
uuid-types-1.0.3: using precompiled package
uuid-1.3.12: using precompiled package
cassandra-cql-0.5.0.2: download
cassandra-cql-0.5.0.2: configure
cassandra-cql-0.5.0.2: build
cassandra-cql-0.5.0.2: copy/register
test-cassandra-cql-0.1.0.0: configure
Configuring test-cassandra-cql-0.1.0.0...
test-cassandra-cql-0.1.0.0: build
Preprocessing executable 'test-cassandra-cql' for
test-cassandra-cql-0.1.0.0...
[1 of 1] Compiling Main ( src/Main.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.5.0/build/test-cassandra-cql/test-cassandra-cql-tmp/Main.o )
Linking .stack-work/dist/x86_64-linux/Cabal-1.22.5.0/build/test-cassandra-cql/test-cassandra-cql ...
test-cassandra-cql-0.1.0.0: copy/register
Installing executable(s) in
/tmp/test-cassandra-cql/.stack-work/install/x86_64-linux/lts-6.9/7.10.3/bin
Completed 8 action(s).
/tmp/test-cassandra-cql λ
Cool, now it’s installed and we are ready to write some code. Let’s be lazy and peruse the github link I found on cassandra-cql’s Hackage page. At the github page I see something like this:
Database/Cassandra executeTrans now takes consistency as a parameter. 7 months ago
tests executeTrans now takes consistency as a parameter. 7 months ago
.gitignore Use Data.Pool for connection pooling. 2 years ago
.travis.yml Lower casing language name in travis script. a year ago
LICENSE Checkpoint: It installs and more stuff works. Results not typed yet. 3 years ago
README.md Edits to readme. a year ago
Setup.hs Add Setup.hs 3 years ago
cassandra-cql.cabal Fixed map serialization 11 months ago
changelog.md ver 0.5.0.2 change: fix incorrect upper bound on base package. a year ago
No examples directory, but tests directories usually have something useful. When I go there I see:
.gitignore Added script for creating keyspace. a year ago
Main.hs Fixed map serialization 11 months ago
create_keyspace.cql Added script for creating keyspace. a year ago
example-autocreate-keyspace.hs remove unused code a year ago
example-trans.hs executeTrans now takes consistency as a parameter. 7 months ago
example.hs Fixing authentication for CQL binary protocol v2. a year ago
test-decimal.hs Added PasswordAuthenticator and associated code. 2 years ago
test-double.hs Added PasswordAuthenticator and associated code. 2 years ago
test-float.hs Added PasswordAuthenticator and associated code. 2 years ago
test-inet.hs Added PasswordAuthenticator and associated code. 2 years ago
test-list.hs Added PasswordAuthenticator and associated code. 2 years ago
test-pool.hs export newPool' which allows overriding pool configuration defaults 2 years ago
test-set.hs Added PasswordAuthenticator and associated code. 2 years ago
test-timestamp.hs Added PasswordAuthenticator and associated code. 2 years ago
test-timeuuid.hs Added PasswordAuthenticator and associated code. 2 years ago
test-varint.hs Added PasswordAuthenticator and associated code. 2 years ago
Ah, there’s an example.hs and here are it’s contents:
{-# LANGUAGE OverloadedStrings, DataKinds #-}
import Database.Cassandra.CQL
import Control.Monad
import Control.Monad.CatchIO
import Control.Monad.Trans (liftIO)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as C
import Data.Text (Text)
import qualified Data.Text as T
import Data.UUID
import System.Random
dropSongs :: Query Schema () ()
dropSongs = "drop table songs"
createSongs :: Query Schema () ()
createSongs = "create table songs (id uuid PRIMARY KEY, title ascii, artist varchar, femaleSinger boolean, timesPlayed int, comment text)"
insertSong :: Query Write (UUID, ByteString, Text, Bool, Int, Maybe Text) ()
insertSong = "insert into songs (id, title, artist, femaleSinger, timesPlayed, comment) values (?, ?, ?, ?, ?, ?)"
getSongs :: Query Rows () (UUID, ByteString, Text, Bool, Int, Maybe Text)
getSongs = "select id, title, artist, femaleSinger, timesPlayed, comment from songs"
getOneSong :: Query Rows UUID (Text, Int)
getOneSong = "select artist, timesPlayed from songs where id=?"
ignoreDropFailure :: Cas () -> Cas ()
ignoreDropFailure code = code `catch` \exc -> case exc of
ConfigError _ _ -> return () -- Ignore the error if the table doesn't exist
Invalid _ _ -> return ()
_ -> throw exc
main = do
-- let auth = Just (PasswordAuthenticator "cassandra" "cassandra")
let auth = Nothing
{-
Assuming a 'test' keyspace already exists. Here's some CQL to create it:
CREATE KEYSPACE test WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' };
-}
pool <- newPool [("localhost", "9042")] "test" auth -- servers, keyspace, maybe auth
runCas pool $ do
ignoreDropFailure $ liftIO . print =<< executeSchema QUORUM dropSongs ()
liftIO . print =<< executeSchema QUORUM createSongs ()
u1 <- liftIO randomIO
u2 <- liftIO randomIO
u3 <- liftIO randomIO
executeWrite QUORUM insertSong (u1, "La Grange", "ZZ Top", False, 2, Nothing)
executeWrite QUORUM insertSong (u2, "Your Star", "Evanescence", True, 799, Nothing)
executeWrite QUORUM insertSong (u3, "Angel of Death", "Slayer", False, 50, Just "Singer Tom Araya")
songs <- executeRows QUORUM getSongs ()
liftIO $ forM_ songs $ \(uuid, title, artist, female, played, mComment) -> do
putStrLn ""
putStrLn $ "id : "++show uuid
putStrLn $ "title : "++C.unpack title
putStrLn $ "artist : "++T.unpack artist
putStrLn $ "female singer : "++show female
putStrLn $ "times played : "++show played
putStrLn $ "comment : "++show mComment
liftIO $ putStrLn ""
liftIO . print =<< executeRow QUORUM getOneSong u2
Replace everying in Main.hs of our project we created with the above. Now we can try stack build
again:
test-cassandra-cql-0.1.0.0: configure
Configuring test-cassandra-cql-0.1.0.0...
test-cassandra-cql-0.1.0.0: build
Preprocessing executable 'test-cassandra-cql' for
test-cassandra-cql-0.1.0.0...
/tmp/test-cassandra-cql/src/Main.hs:5:8:
Could not find module ‘Control.Monad.CatchIO’
It is a member of the hidden package ‘MonadCatchIO-transformers-0.3.1.3@Monad_Jr5N1jwqWOiDadnjgHIYSh’.
Perhaps you need to add ‘MonadCatchIO-transformers’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:6:8:
Could not find module ‘Control.Monad.Trans’
It is a member of the hidden package ‘monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH’.
Perhaps you need to add ‘monads-tf’ to the build-depends in your .cabal file.
It is a member of the hidden package ‘mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8’.
Perhaps you need to add ‘mtl’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:8:18:
Could not find module ‘Data.ByteString.Char8’
It is a member of the hidden package ‘bytestring-0.10.6.0@bytes_6VWy06pWzJq9evDvK2d4w6’.
Perhaps you need to add ‘bytestring’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:10:18:
Could not find module ‘Data.Text’
It is a member of the hidden package ‘text-1.2.2.1@text_HmqVQnZSpjaC156ABqPhne’.
Perhaps you need to add ‘text’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:11:8:
Could not find module ‘Data.UUID’
It is a member of the hidden package ‘uuid-1.3.12@uuid_BjRErtmELFC2d9F8IOzq95’.
Perhaps you need to add ‘uuid’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:12:8:
Could not find module ‘System.Random’
It is a member of the hidden package ‘random-1.1@rando_9Kgekc9yEaLHLNUuw6paWL’.
Perhaps you need to add ‘random’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
-- While building package test-cassandra-cql-0.1.0.0 using:
/home/cody/.stack/setup-exe-cache/x86_64-linux/setup-Simple-Cabal-1.22.5.0-ghc-7.10.3 --builddir=.stack-work/dist/x86_64-linux/Cabal-1.22.5.0 build exe:test-cassandra-cql --ghc-options " -ddump-hi -ddump-to-file"
Process exited with code: ExitFailure 1
Uh oh, that looks kind of scary. All it’s saying though is that we don’t have those packages listed in the build-depends section of our cabal file which currently looks like:
name: test-cassandra-cql
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/test-cassandra-cql#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable test-cassandra-cql
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
cassandra-cql,
Let’s update it to include all of those dependencies:
name: test-cassandra-cql
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/test-cassandra-cql#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable test-cassandra-cql
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
cassandra-cql,
MonadCatchIO-transformers,
monads-tf,
bytestring,
text,
uuid,
random
And then run stack build
again:
/tmp/test-cassandra-cql λ stack build
test-cassandra-cql-0.1.0.0: configure
Configuring test-cassandra-cql-0.1.0.0...
test-cassandra-cql-0.1.0.0: build
Preprocessing executable 'test-cassandra-cql' for
test-cassandra-cql-0.1.0.0...
[1 of 1] Compiling Main ( src/Main.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.5.0/build/test-cassandra-cql/test-cassandra-cql-tmp/Main.o )
Linking .stack-work/dist/x86_64-linux/Cabal-1.22.5.0/build/test-cassandra-cql/test-cassandra-cql ...
test-cassandra-cql-0.1.0.0: copy/register
Installing executable(s) in
/tmp/test-cassandra-cql/.stack-work/install/x86_64-linux/lts-6.9/7.10.3/bin
It worked! The real question is: Why was I using stack build when next we’re just going to use stack ghci. Ah well, time to use stack ghci
and try to run our main function:
/tmp/test-cassandra-cql λ stack ghci
Using main module: 1. Package `test-cassandra-cql' component exe:test-cassandra-cql with main-is file: /tmp/test-cassandra-cql/src/Main.hs
Configuring GHCi with the following packages: test-cassandra-cql
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Ok, modules loaded: none.
[1 of 1] Compiling Main ( /tmp/test-cassandra-cql/src/Main.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
failed to create a session due to temporary error (will retry) : NoAvailableServers
failed to create a session due to permanent error (will rethrow) : user interrupt
Interrupted.
Uh oh, we forgot to actually make a Cassandra server available. We can use Docker to quickly do that. If you don’t have Docker installed check out Docker’s installation page. So open up another terminal and type:
docker run -p 9042:9042 -p 9160:9160 -it --name some-cassandra cassandra:latest
It should look something like this:
/tmp/test-cassandra-cql λ docker run -p 9042:9042 -p 9160:9160 -it --name some-cassandra cassandra:latest
Unable to find image 'cassandra:latest' locally
latest: Pulling from library/cassandra
Digest: sha256:201f7a0fd29490032435945ee4985c0aa69eff36b2882e9a6d4b436eb140b1cc
Status: Downloaded newer image for cassandra:latest
INFO 15:57:05 Configuration location: file:/etc/cassandra/cassandra.yaml
... snip (random cassandra logs) ...
INFO 15:57:18 Scheduling approximate time-check task with a precision of 10 milliseconds
INFO 15:57:18 Created default superuser role 'cassandra'
Now if we go back to the other terminal and run our main function let’s see what happens:
*Main> main
failed to create a session due to temporary error (will retry) : LocalProtocolError "unexpected version 4" "<startup>"
failed to create a session due to permanent error (will rethrow) : user interrupt
Uh oh, it’s an unexpected version. Maybe cassandra-cql doesn’t support latest version of cassandra? Let’s check the docs and the source repo. On the repo’s readme.md it says:
“Haskell client for Cassandra’s CQL binary protocol v2”
I believe the latest Cassandra uses CQL version 3.3, so we’ll need a little older Cassandra.
Let’s check what tags are available on the Docker Cassandra page
2.1.15, 2.1 (2.1/Dockerfile)
2.2.7, 2.2, 2 (2.2/Dockerfile)
3.0.8, 3.0 (3.0/Dockerfile)
3.7, 3, latest (3.7/Dockerfile)
Hm, let’s guess and try 3.0.8.
/tmp/test-cassandra-cql λ docker rm -f some-cassandra && docker run -p 9042:9042 -p 9160:9160 -it --name some-cassandra cassandra:3.0.8
some-cassandra
INFO 18:07:43 Configuration location: file:/etc/cassandra/cassandra.yaml
INFO 18:07:53 Starting listening for CQL clients on /0.0.0.0:9042 (unencrypted)...
... snip (random Cassandra logs) ...
INFO 18:07:53 Not starting RPC server as requested. Use JMX (StorageService->startRPCServer()) or nodetool (enablethrift) to start it
INFO 18:07:55 Created default superuser role 'cassandra'
Then try running our main function again:
*Main> main
failed to create a session due to temporary error (will retry) : LocalProtocolError "unexpected version 4" "<startup>"
I’m not sure how CQL versions/Cassandra correspond so I’m just going to try the oldest tag and see if it works.
/tmp/test-cassandra-cql λ docker rm -f some-cassandra && docker run -p 9042:9042 -p 9160:9160 -it --name some-cassandra cassandra:2.1
some-cassandra
Unable to find image 'cassandra:2.1' locally
2.1: Pulling from library/cassandra
357ea8c3d80b: Already exists
25eb4fd61cd9: Already exists
a21cf6fac262: Already exists
... snip (you know the rest by now) ...
Trying to run main again:
*Main> main
failed to create a session due to permanent error (will rethrow) : Invalid "Keyspace 'test' does not exist" "USE test"
failed to create a session due to permanent error (will rethrow) : user interrupt
Interrupted.
Awesome! We’re almost there, just need to create the keyspace. Can we do that with cassandra-cql? I searched “keyspace” on the cassandra-cql github and one of the results was “tests/example-autocreate-keyspace.hs”. It looks like the example we copy pasted except it auto creates the keyspace. So copy the below into your main.hs
:
{-# LANGUAGE OverloadedStrings, DataKinds #-}
import Database.Cassandra.CQL
import Control.Monad
import Control.Monad.CatchIO
import Control.Monad.Trans (liftIO)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as C
import Data.Text (Text)
import qualified Data.Text as T
import Data.UUID
import System.Random
dropSongs :: Query Schema () ()
dropSongs = "drop table songs"
createSongs :: Query Schema () ()
createSongs = "create table songs (id uuid PRIMARY KEY, title ascii, artist varchar, femaleSinger boolean, timesPlayed int, comment text)"
insertSong :: Query Write (UUID, ByteString, Text, Bool, Int, Maybe Text) ()
insertSong = "insert into songs (id, title, artist, femaleSinger, timesPlayed, comment) values (?, ?, ?, ?, ?, ?)"
getSongs :: Query Rows () (UUID, ByteString, Text, Bool, Int, Maybe Text)
getSongs = "select id, title, artist, femaleSinger, timesPlayed, comment from songs"
getOneSong :: Query Rows UUID (Text, Int)
getOneSong = "select artist, timesPlayed from songs where id=?"
ignoreDropFailure :: Cas () -> Cas ()
ignoreDropFailure code = code `catch` \exc -> case exc of
ConfigError _ _ -> return () -- Ignore the error if the table doesn't exist
Invalid _ _ -> return ()
_ -> throw exc
main = do
-- let auth = Just (PasswordAuthenticator "cassandra" "cassandra")
let auth = Nothing
-- this config will automatically run keyspace creation cql script during each connection initializationj
-- suitable for a development purposes
let ksCfg = "CREATE KEYSPACE IF NOT EXISTS test1 WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' };"
let poolCfg = (defaultConfig [("localhost", "9042")] "test1" auth){ piKeyspaceConfig = Just ksCfg}
pool <- newPool' poolCfg -- servers, keyspace, maybe auth
runCas pool $ do
ignoreDropFailure $ liftIO . print =<< executeSchema QUORUM dropSongs ()
liftIO . print =<< executeSchema QUORUM createSongs ()
u1 <- liftIO randomIO
u2 <- liftIO randomIO
u3 <- liftIO randomIO
executeWrite QUORUM insertSong (u1, "La Grange", "ZZ Top", False, 2, Nothing)
executeWrite QUORUM insertSong (u2, "Your Star", "Evanescence", True, 799, Nothing)
executeWrite QUORUM insertSong (u3, "Angel of Death", "Slayer", False, 50, Just "Singer Tom Araya")
songs <- executeRows QUORUM getSongs ()
liftIO $ forM_ songs $ \(uuid, title, artist, female, played, mComment) -> do
putStrLn ""
putStrLn $ "id : "++show uuid
putStrLn $ "title : "++C.unpack title
putStrLn $ "artist : "++T.unpack artist
putStrLn $ "female singer : "++show female
putStrLn $ "times played : "++show played
putStrLn $ "comment : "++show mComment
liftIO $ putStrLn ""
liftIO . print =<< executeRow QUORUM getOneSong u2
Then we can reload in stack ghci
and re-run our main function:
Prelude> :r
[1 of 1] Compiling Main ( /tmp/test-cassandra-cql/src/Main.hs, interpreted )
/tmp/test-cassandra-cql/src/Main.hs:43:73:
parse error on input ‘KeyspaceConfig’
Failed, modules loaded: none.
Prelude> 1
I know from experience that at this point it’s probably because the version on github I copied the example from and the version on Hackage differ. Sure enough after checking I see the Hackage version is at 0.5.0.2 and the github version is at 0.6. I’m just going to use the github version, luckily stack makes that pretty easy.
Here is our current stack.yaml
:
flags: {}
extra-package-dbs: []
packages:
- '.'
extra-deps:
- cassandra-cql-0.5.0.2
resolver: lts-6.9
I’ve been busy doing Go programming at work and can’t recall the git syntax for Stack, so I’ll refer to this big FAQ page they have. In about 15 seconds I see “I need to use a package (or version of a package) that is not available on Hackage, what should I do?” and then:
To install packages directly from a Git repository, use e.g.:
resolver: lts-2.10
packages:
- location:
git: https://github.com/githubuser/reponame.git
commit: somecommitID
So we can update our stack.yaml
from above to:
flags: {}
extra-package-dbs: []
packages:
- '.'
- location:
git: https://github.com/the-real-blackh/cassandra-cql.git
commit: a34c7448630a25400ad20d42c5faca248f6fe644
resolver: lts-6.9
Then we can restart stack ghci:
Prelude> :r
[1 of 1] Compiling Main ( /tmp/test-cassandra-cql/src/Main.hs, interpreted )
/tmp/test-cassandra-cql/src/Main.hs:43:73:
‘piKeyspaceConfig’ is not a (visible) constructor field name
Failed, modules loaded: none.
Prelude> :q
Leaving GHCi.
/tmp/test-cassandra-cql λ stack ghci
cassandra-cql-0.5.0.2: unregistering
cassandra-cql-0.6: configure
cassandra-cql-0.6: build
cassandra-cql-0.6: copy/register
test-cassandra-cql-0.1.0.0: configure
test-cassandra-cql-0.1.0.0: build
test-cassandra-cql-0.1.0.0: copy/register
Completed 2 action(s).
Using main module: 1. Package `test-cassandra-cql' component exe:test-cassandra-cql with main-is file: /tmp/test-cassandra-cql/src/Main.hs
Configuring GHCi with the following packages: test-cassandra-cql, cassandra-cql
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:3:23: Warning:
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:169:8:
Ambiguous module name ‘Control.Monad.Reader’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:170:8:
Ambiguous module name ‘Control.Monad.State’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:171:18:
Ambiguous module name ‘Control.Monad.RWS’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:172:18:
Ambiguous module name ‘Control.Monad.Error’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:173:18:
Ambiguous module name ‘Control.Monad.Writer’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
Failed, modules loaded: none.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:3:23: Warning:
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:169:8:
Ambiguous module name ‘Control.Monad.Reader’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:170:8:
Ambiguous module name ‘Control.Monad.State’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:171:18:
Ambiguous module name ‘Control.Monad.RWS’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:172:18:
Ambiguous module name ‘Control.Monad.Error’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:173:18:
Ambiguous module name ‘Control.Monad.Writer’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/src/Main.hs:6:8:
Ambiguous module name ‘Control.Monad.Trans’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
Failed, modules loaded: none.
<no location info>:
Could not find module ‘Database.Cassandra.CQL’
It is a member of the hidden package ‘cassandra-cql-0.6@cassa_FMk92m2DzAC8Q6AfWWp6qA’.
Whoa, it partially worked but then barfed on an error I’ve both not seen before and don’t currently know what it is. I have a hunch it’s something to do with multiple but different versions of cassandra-cql’s dependencies. So I’ll try something quick and easy, the equivalent of “turn it off and then on again”:
/tmp/test-cassandra-cql λ rm -r .stack-work/
/tmp/test-cassandra-cql λ stack build
cassandra-cql-0.6: configure
cassandra-cql-0.6: build
cassandra-cql-0.6: copy/register
test-cassandra-cql-0.1.0.0: configure
test-cassandra-cql-0.1.0.0: build
test-cassandra-cql-0.1.0.0: copy/register
Completed 2 action(s).
/tmp/test-cassandra-cql λ stack ghci
Using main module: 1. Package `test-cassandra-cql' component exe:test-cassandra-cql with main-is file: /tmp/test-cassandra-cql/src/Main.hs
Configuring GHCi with the following packages: test-cassandra-cql, cassandra-cql
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:3:23: Warning:
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:169:8:
Ambiguous module name ‘Control.Monad.Reader’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:170:8:
Ambiguous module name ‘Control.Monad.State’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:171:18:
Ambiguous module name ‘Control.Monad.RWS’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:172:18:
Ambiguous module name ‘Control.Monad.Error’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:173:18:
Ambiguous module name ‘Control.Monad.Writer’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
Failed, modules loaded: none.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:3:23: Warning:
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:169:8:
Ambiguous module name ‘Control.Monad.Reader’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:170:8:
Ambiguous module name ‘Control.Monad.State’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:171:18:
Ambiguous module name ‘Control.Monad.RWS’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:172:18:
Ambiguous module name ‘Control.Monad.Error’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:173:18:
Ambiguous module name ‘Control.Monad.Writer’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
/tmp/test-cassandra-cql/src/Main.hs:6:8:
Ambiguous module name ‘Control.Monad.Trans’:
it was found in multiple packages:
mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8 monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH
Failed, modules loaded: none.
<no location info>:
Could not find module ‘Database.Cassandra.CQL’
It is a member of the hidden package ‘cassandra-cql-0.6@cassa_FMk92m2DzAC8Q6AfWWp6qA’.
Prelude>
After some reflection, figured out that this issue is caused by these libraries being in our Cabal file:
name: test-cassandra-cql
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/test-cassandra-cql#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable test-cassandra-cql
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
cassandra-cql,
MonadCatchIO-transformers,
monads-tf,
bytestring,
text,
uuid,
random
Update it to:
name: test-cassandra-cql
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/test-cassandra-cql#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable test-cassandra-cql
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
cassandra-cql,
MonadCatchIO-transformers,
monads-tf,
bytestring,
text,
uuid,
random
Then run stack ghci
again and you’ll see those errors have gone away (though we have a lot of warnings):
/tmp/test-cassandra-cql λ stack ghci
test-cassandra-cql-0.1.0.0: build
-- While building package test-cassandra-cql-0.1.0.0 using:
/home/cody/.stack/setup-exe-cache/x86_64-linux/setup-Simple-Cabal-1.22.5.0-ghc-7.10.3 --builddir=.stack-work/dist/x86_64-linux/Cabal-1.22.5.0 build exe:test-cassandra-cql --ghc-options " -ddump-hi -ddump-to-file"
Process exited with code: ExitFailure 1
Logs have been written to: /tmp/test-cassandra-cql/.stack-work/logs/test-cassandra-cql-0.1.0.0.log
Preprocessing executable 'test-cassandra-cql' for
test-cassandra-cql-0.1.0.0...
/tmp/test-cassandra-cql/src/Main.hs:5:8:
Could not find module ‘Control.Monad.CatchIO’
It is a member of the hidden package ‘MonadCatchIO-transformers-0.3.1.3@Monad_Jr5N1jwqWOiDadnjgHIYSh’.
Perhaps you need to add ‘MonadCatchIO-transformers’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:6:8:
Could not find module ‘Control.Monad.Trans’
It is a member of the hidden package ‘monads-tf-0.1.0.3@monad_5M6wZtP1TpiAkFGzni5tIH’.
Perhaps you need to add ‘monads-tf’ to the build-depends in your .cabal file.
It is a member of the hidden package ‘mtl-2.2.1@mtl_Aue4leSeVkpKLsfHIV51E8’.
Perhaps you need to add ‘mtl’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:8:18:
Could not find module ‘Data.ByteString.Char8’
It is a member of the hidden package ‘bytestring-0.10.6.0@bytes_6VWy06pWzJq9evDvK2d4w6’.
Perhaps you need to add ‘bytestring’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:10:18:
Could not find module ‘Data.Text’
It is a member of the hidden package ‘text-1.2.2.1@text_HmqVQnZSpjaC156ABqPhne’.
Perhaps you need to add ‘text’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
/tmp/test-cassandra-cql/src/Main.hs:11:8:
Could not find module ‘Data.UUID’
It is a member of the hidden package ‘uuid-1.3.12@uuid_BjRErtmELFC2d9F8IOzq95’.
Perhaps you need to add ‘uuid’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
Warning: build failed, but optimistically launching GHCi anyway
Using main module: 1. Package `test-cassandra-cql' component exe:test-cassandra-cql with main-is file: /tmp/test-cassandra-cql/src/Main.hs
Configuring GHCi with the following packages: test-cassandra-cql, cassandra-cql
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:3:23: Warning:
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
[1 of 1] Compiling Database.Cassandra.CQL ( /tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs, interpreted )
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:172:1: Warning:
Module ‘Control.Monad.Error’ is deprecated:
Use Control.Monad.Except instead
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:303:29: Warning:
In the use of type constructor or class ‘Control.Monad.Error.Error’
(imported from Control.Monad.Error, but defined in transformers-0.4.2.0:Control.Monad.Trans.Error):
Deprecated: "Use Control.Monad.Trans.Except instead"
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:303:77: Warning:
In the use of type constructor or class ‘Control.Monad.Error.ErrorT’
(imported from Control.Monad.Error, but defined in transformers-0.4.2.0:Control.Monad.Trans.Error):
Deprecated: "Use Control.Monad.Trans.Except instead"
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:837:38: Warning:
Unticked promoted constructor: ‘Schema’.
Use ‘'Schema’ instead of ‘Schema’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:843:55: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1788:22: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1799:23: Warning:
Unticked promoted constructor: ‘Write’.
Use ‘'Write’ instead of ‘Write’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1816:21: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1823:59: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1835:23: Warning:
Unticked promoted constructor: ‘Write’.
Use ‘'Write’ instead of ‘Write’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1847:24: Warning:
Unticked promoted constructor: ‘Schema’.
Use ‘'Schema’ instead of ‘Schema’.
/tmp/test-cassandra-cql/.stack-work/downloaded/MXPl4BU8uauT/Database/Cassandra/CQL.hs:1859:24: Warning:
Unticked promoted constructor: ‘Schema’.
Use ‘'Schema’ instead of ‘Schema’.
Ok, modules loaded: Database.Cassandra.CQL.
[2 of 2] Compiling Main ( /tmp/test-cassandra-cql/src/Main.hs, interpreted )
/tmp/test-cassandra-cql/src/Main.hs:14:20: Warning:
Unticked promoted constructor: ‘Schema’.
Use ‘'Schema’ instead of ‘Schema’.
/tmp/test-cassandra-cql/src/Main.hs:17:22: Warning:
Unticked promoted constructor: ‘Schema’.
Use ‘'Schema’ instead of ‘Schema’.
/tmp/test-cassandra-cql/src/Main.hs:20:21: Warning:
Unticked promoted constructor: ‘Write’.
Use ‘'Write’ instead of ‘Write’.
/tmp/test-cassandra-cql/src/Main.hs:23:19: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
/tmp/test-cassandra-cql/src/Main.hs:26:21: Warning:
Unticked promoted constructor: ‘Rows’.
Use ‘'Rows’ instead of ‘Rows’.
Ok, modules loaded: Database.Cassandra.CQL, Main.
*Main Database.Cassandra.CQL>
Now, we can try to run our main function again:
*Main Database.Cassandra.CQL> main
(CREATED,Keyspace "TABLE",Table "test1")
id : 8a33cc5c-5d1c-464f-ab3e-19c491f240d8
title : Your Star
artist : Evanescence
female singer : True
times played : 799
comment : Nothing
id : 662f407a-7362-47c8-bc37-b859847a031a
title : La Grange
artist : ZZ Top
female singer : False
times played : 2
comment : Nothing
id : 017e4971-88a2-48f1-b7c3-98cde924a6cc
title : Angel of Death
artist : Slayer
female singer : False
times played : 50
comment : Just "Singer Tom Araya"
Just ("Evanescence",799)
And success!
Part 2 will be a retrospective reflecting on this process and how connecting to Cassandra in Haskell could be made easier for someone who might not have the luxury of time and patience that I had to get this working.