Last active
November 18, 2018 21:25
-
-
Save tfausak/cc381ddd0ddd8304cc3eae6250966f55 to your computer and use it in GitHub Desktop.
Analyzes 2018 state of Haskell survey responses. https://github.com/tfausak/tfausak.github.io/pull/148
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env stack | |
-- stack --resolver lts-10.0 script | |
module Main | |
( main | |
) | |
where | |
import qualified Control.Monad as Monad | |
import qualified Data.ByteString.Lazy as LazyByteString | |
import qualified Data.Colour.SRGB as Color | |
import qualified Data.Csv as Csv | |
import qualified Data.Maybe as Maybe | |
import qualified Data.Set as Set | |
import qualified Data.Text as Text | |
import qualified Data.Time as Time | |
import qualified Data.Vector as Vector | |
import qualified Graphics.Rendering.Chart.Backend.Diagrams as Diagrams | |
import qualified Graphics.Rendering.Chart.Easy as Chart | |
import qualified System.Directory as Directory | |
import qualified System.Environment as Environment | |
import qualified System.FilePath as FilePath | |
import qualified System.IO as IO | |
import qualified Text.Printf as Printf | |
-- Unfortunately there were a large number of bogus submissions. Of the 5,094 | |
-- submissions received, 3,417 of them appear to be submitted by a script. | |
-- Fortunately they are pretty easy to spot. This function is responsible for | |
-- identifying them. | |
shouldIgnore :: Response -> Bool | |
shouldIgnore response = | |
contains "Linux" (responseQ25 response) -- Which platforms do you target? | |
&& contains "GHC" (responseQ28 response) -- Which Haskell compilers do you use? | |
&& contains "Stack" (responseQ30 response) -- Which installation methods do you use for your Haskell compiler? | |
&& is "Yes" (responseQ32 response) -- Has upgrading your Haskell compiler broken your code in the last year? | |
&& is "I dislike it" (responseQ36 response) -- How do you feel about the new GHC release schedule? | |
&& contains "Stack" (responseQ41 response) -- Which build tools do you use for Haskell? | |
&& Maybe.isNothing (responseQ95 response) -- Which country do you live in? | |
&& Maybe.isNothing (responseQ98 response) -- Do you identify as transgender? | |
&& Maybe.isNothing (responseQ99 response) -- Are you a student? | |
&& Maybe.isNothing (responseQ104 response) -- How many years have you been coding professionally? | |
&& Maybe.isNothing (responseQ105 response) -- Do you code as a hobby? | |
&& Maybe.isNothing (responseQ110 response) -- Do you have any feedback about the survey itself? | |
main :: IO () | |
main = do | |
arguments <- Environment.getArgs | |
file <- case arguments of | |
[file] -> pure file | |
_ -> fail $ "unexpected arguments: " ++ show arguments | |
contents <- LazyByteString.readFile file | |
allResponses <- either fail pure $ Csv.decode Csv.HasHeader contents | |
let responses = Vector.filter (not . shouldIgnore) allResponses | |
let | |
numAll = Vector.length allResponses | |
numGood = Vector.length responses | |
numBad = numAll - numGood | |
Printf.printf "%d good, %d bad, %d total\n" numGood numBad numAll | |
Directory.createDirectoryIfMissing True outputDirectory | |
handle <- IO.openFile | |
(FilePath.combine outputDirectory $ FilePath.addExtension "index" "html") | |
IO.WriteMode | |
IO.hPutStrLn handle "<!doctype html>" | |
IO.hPutStrLn handle "<html>" | |
IO.hPutStrLn handle "<meta charset='utf-8'>" | |
IO.hPutStrLn handle "<title>2018 state of Haskell survey results</title>" | |
IO.hPutStrLn handle "</head>" | |
IO.hPutStrLn handle "<body>" | |
IO.hPutStrLn handle "<h1>2018 state of Haskell survey results</h1>" | |
fonts <- Diagrams.loadSansSerifFonts | |
let | |
options = Chart.set Diagrams.fo_fonts (pure fonts) Chart.def | |
makeBar = makeBarWith handle options responses | |
makeLikertBar = makeLikertBarWith handle options responses | |
makeMultipleBar = makeMultipleBarWith handle options responses | |
makeRatingBar = makeRatingBarWith handle options responses | |
makeSingleBar = makeSingleBarWith handle options responses | |
makeBar | |
(dateToDay . responseQ1) | |
1 | |
"Submission date" | |
[ (Shown, "Nov 1", "2018-11-01", (<= Time.fromGregorian 2018 11 1)) | |
, (Shown, "Nov 2", "2018-11-02", (== Time.fromGregorian 2018 11 2)) | |
, (Shown, "Nov 3", "2018-11-03", (== Time.fromGregorian 2018 11 3)) | |
, (Shown, "Nov 4", "2018-11-04", (== Time.fromGregorian 2018 11 4)) | |
, (Shown, "Nov 5", "2018-11-05", (== Time.fromGregorian 2018 11 5)) | |
, (Shown, "Nov 6", "2018-11-06", (== Time.fromGregorian 2018 11 6)) | |
, (Shown, "Nov 7", "2018-11-07", (== Time.fromGregorian 2018 11 7)) | |
, (Shown, "Nov 8", "2018-11-08", (== Time.fromGregorian 2018 11 8)) | |
, (Shown, "Nov 9", "2018-11-09", (== Time.fromGregorian 2018 11 9)) | |
, (Shown, "Nov 10", "2018-11-10", (== Time.fromGregorian 2018 11 10)) | |
, (Shown, "Nov 11", "2018-11-11", (== Time.fromGregorian 2018 11 11)) | |
, (Shown, "Nov 12", "2018-11-12", (== Time.fromGregorian 2018 11 12)) | |
, (Shown, "Nov 13", "2018-11-13", (== Time.fromGregorian 2018 11 13)) | |
, (Shown, "Nov 14", "2018-11-14", (>= Time.fromGregorian 2018 11 14)) | |
] | |
makeRatingBar responseQ2 2 "Haskell usage" | |
makeSingleBar | |
responseQ3 | |
3 | |
"Do you use Haskell?" | |
[ (Shown, "Yes", "Yes") | |
, (Shown, "No, but I used to", "No, but I used to") | |
, (Shown, "No, I never have", "No, I never have") | |
] | |
makeSingleBar | |
responseQ4 | |
4 | |
"If you stopped using Haskell, how long did you use it before you stopped?" | |
[ (Shown, "Less than 1 day", "Less than 1 day") | |
, (Shown, "1 day to 1 week", "1 day to 1 week") | |
, (Shown, "1 week to 1 month", "1 week to 1 month") | |
, (Shown, "1 month to 1 year", "1 month to 1 year") | |
, (Shown, "More than 1 year", "More than 1 year") | |
] | |
makeMultipleBar | |
responseQ5 | |
5 | |
"If you do not use Haskell, why not?" | |
[ (Shown, "Company doesn't use", "My company doesn't use Haskell") | |
, (Shown, "Hard to learn", "Haskell is too hard to learn") | |
, ( Shown | |
, "Bad documentation" | |
, "Haskell's documentation is not good enough" | |
) | |
, (Shown, "Missing tooling", "Haskell lacks critical tools") | |
, (Shown, "Missing libraries", "Haskell lacks critical libraries") | |
, ( Hidden | |
, "Missing platforms" | |
, "Haskell does not support the platforms I need" | |
) | |
, (Hidden, "Bad performance", "Haskell's performance is not good enough") | |
, (Hidden, "Missing features", "Haskell lacks critical features") | |
] | |
makeSingleBar | |
responseQ7 | |
7 | |
"How long have you been using Haskell?" | |
[ (Shown, "<1d", "Less than 1 day") | |
, (Shown, "1d-1w", "1 day to 1 week") | |
, (Shown, "1w-1m", "1 week to 1 month") | |
, (Shown, "1m-1y", "1 month to 1 year") | |
, (Shown, "1y-2y", "1 year to 2 years") | |
, (Shown, "2y-3y", "2 years to 3 years") | |
, (Shown, "3y-4y", "3 years to 4 years") | |
, (Shown, "4y-5y", "4 years to 5 years") | |
, (Shown, ">5y", "More than 5 years") | |
] | |
makeSingleBar | |
responseQ8 | |
8 | |
"How frequently do you use Haskell?" | |
[ (Shown, "Daily", "Daily") | |
, (Shown, "Weekly", "Weekly") | |
, (Shown, "Monthly", "Monthly") | |
, (Shown, "Yearly", "Yearly") | |
, (Shown, "Rarely", "Rarely") | |
] | |
makeSingleBar | |
responseQ9 | |
9 | |
"How would you rate your proficiency in Haskell?" | |
[ (Shown, "Beginner", "Beginner") | |
, (Shown, "Intermediate", "Intermediate") | |
, (Shown, "Advanced", "Advanced") | |
, (Shown, "Expert", "Expert") | |
, (Shown, "Master", "Master") | |
] | |
makeMultipleBar | |
responseQ10 | |
10 | |
"Where do you use Haskell?" | |
[ (Shown, "Home", "Home") | |
, (Shown, "Work", "Work") | |
, (Shown, "School", "School") | |
] | |
makeSingleBar | |
responseQ11 | |
11 | |
"Do you use Haskell at work?" | |
[ (Shown, "Yes, most of the time", "Yes, most of the time") | |
, (Shown, "Yes, some of the time", "Yes, some of the time") | |
, (Shown, "No, but my company does", "No, but my company does") | |
, (Shown, "No", "No") | |
] | |
makeMultipleBar | |
responseQ12 | |
12 | |
"If you do not use Haskell at work, why not?" | |
[ (Shown, "Company doesn't use", "My company doesn't use Haskell") | |
, (Shown, "Hard to learn", "Haskell is too hard to learn") | |
, (Shown, "Hard to hire", "It is too hard to hire Haskell developers") | |
, (Shown, "Missing tooling", "Haskell lacks critical tools") | |
, ( Shown | |
, "Missing platforms" | |
, "Haskell does not support the platforms I need" | |
) | |
, (Hidden, "Missing libraries", "Haskell lacks critical libraries") | |
, (Hidden, "Bad performance", "Haskell's performance is not good enough") | |
, ( Hidden | |
, "Bad documentation" | |
, "Haskell's documentation is not good enough" | |
) | |
, (Hidden, "Missing features", "Haskell lacks critical features") | |
] | |
makeMultipleBar | |
responseQ14 | |
14 | |
"Which programming languages other than Haskell are you fluent in?" | |
$ fmap | |
(\(visibility, language) -> (visibility, language, language)) | |
[ (Shown, "Java") | |
, (Shown, "Python") | |
, (Shown, "JavaScript") | |
, (Shown, "C") | |
, (Shown, "C++") | |
, (Shown, "Shell") | |
, (Shown, "Scala") | |
, (Shown, "Ruby") | |
, (Shown, "C#") | |
, (Shown, "TypeScript") | |
, (Hidden, "Rust") | |
, (Hidden, "PHP") | |
, (Hidden, "Assembly") | |
, (Hidden, "Go") | |
, (Hidden, "Clojure") | |
, (Hidden, "Ocaml") | |
, (Hidden, "Perl") | |
, (Hidden, "R") | |
, (Hidden, "Lua") | |
, (Hidden, "F#") | |
, (Hidden, "Erlang") | |
, (Hidden, "Matlab") | |
, (Hidden, "Objective-C") | |
, (Hidden, "Swift") | |
, (Hidden, "Kotlin") | |
, (Hidden, "Groovy") | |
, (Hidden, "VB.NET") | |
, (Hidden, "VBA") | |
, (Hidden, "Julia") | |
, (Hidden, "Hack") | |
] | |
makeMultipleBar | |
responseQ16 | |
16 | |
"Which types of software do you develop with Haskell?" | |
[ (Shown, "CLI", "Command-line programs (CLI)") | |
, (Shown, "Web (API)", "API services (returning non-HTML)") | |
, (Shown, "Data processing", "Data processing") | |
, (Shown, "Libraries", "Libraries or frameworks") | |
, (Shown, "Web (HTML)", "Web services (returning HTML)") | |
, (Shown, "Automation", "Automation or scripts") | |
, (Shown, "Daemon", "Agents or daemons") | |
, (Shown, "GUI", "Desktop programs (GUI)") | |
] | |
makeMultipleBar | |
responseQ18 | |
18 | |
"Which industries do you use Haskell in?" | |
[ (Shown, "Web", "Web") | |
, (Shown, "Finance", "Banking or finance") | |
, (Shown, "Education", "Education") | |
, (Shown, "Retail", "Commerce or retail") | |
, (Shown, "Government", "Government") | |
, (Shown, "Mobile", "Mobile") | |
, (Shown, "Health", "Healthcare or medical") | |
, (Shown, "Gaming", "Gaming") | |
, (Shown, "Embedded", "Embedded") | |
] | |
makeRatingBar responseQ20 20 "Projects" | |
makeSingleBar | |
responseQ21 | |
21 | |
"How many Haskell projects do you contribute to?" | |
[ (Shown, "0", "0") | |
, (Shown, "1", "1") | |
, (Shown, "2-5", "2 to 5") | |
, (Shown, "6-10", "6 to 10") | |
, (Shown, "11-20", "11 to 20") | |
, (Shown, ">20", "More than 20") | |
] | |
makeSingleBar | |
responseQ22 | |
22 | |
"What is the total size of all the Haskell projects you contribute to?" | |
[ (Shown, "<1k", "Less than 1,000 lines of code") | |
, (Shown, "1k-10k", "1,000 lines of code to 9,999 lines of code") | |
, (Shown, "10k-100k", "10,000 lines of code to 99,999 lines of code") | |
, (Shown, ">=100k", "100,000 or more lines of code") | |
] | |
makeMultipleBar | |
responseQ23 | |
23 | |
"Which platforms do you develop Haskell on?" | |
[ (Shown, "Linux", "Linux") | |
, (Shown, "MacOS", "MacOS") | |
, (Shown, "Windows", "Windows") | |
, (Shown, "BSD", "BSD") | |
] | |
makeMultipleBar | |
responseQ25 | |
25 | |
"Which platforms do you target?" | |
[ (Shown, "Linux", "Linux") | |
, (Shown, "MacOS", "MacOS") | |
, (Shown, "Windows", "Windows") | |
, (Shown, "BSD", "BSD") | |
, (Shown, "Android", "Android") | |
, (Shown, "iOS", "iOS") | |
] | |
makeRatingBar responseQ27 27 "Compilers" | |
makeMultipleBar | |
responseQ28 | |
28 | |
"Which Haskell compilers do you use?" | |
[(Shown, "GHC", "GHC"), (Shown, "GHCJS", "GHCJS"), (Shown, "Eta", "Eta")] | |
makeMultipleBar | |
responseQ30 | |
30 | |
"Which installation methods do you use for your Haskell compiler?" | |
[ (Shown, "Stack", "Stack") | |
, (Shown, "Nix", "Nix") | |
, (Shown, "OS", "Operating system package") | |
, (Shown, "Platform", "Haskell Platform") | |
, (Shown, "Binary", "Official binaries") | |
, (Shown, "Source", "Source") | |
, (Shown, "Minimal", "Minimal installer") | |
] | |
makeSingleBar | |
responseQ32 | |
32 | |
"Has upgrading your Haskell compiler broken your code in the last year?" | |
[(Shown, "Yes", "Yes"), (Shown, "No", "No")] | |
makeMultipleBar | |
responseQ33 | |
33 | |
"How has upgrading your Haskell compiler broken your code in the past year?" | |
[ ( Shown | |
, "Expected changes" | |
, "Expected changes, such as the MonadFail proposal" | |
) | |
, (Shown, "Incompatible deps", "Incompatible dependencies") | |
, (Shown, "Unexpected changes", "Unexpected changes") | |
, (Shown, "Compiler bugs", "Compiler bugs") | |
, (Shown, "New warnings", "New warnings") | |
] | |
makeBar | |
responseQ35 | |
35 | |
"Which versions of GHC do you use?" | |
[ (Hidden, "HEAD", "HEAD", contains "HEAD") | |
, (Shown, "8.6", "8.6.x", containsAny ["8.6.1"]) | |
, (Shown, "8.4", "8.4.x", containsAny ["8.4.4", "8.4.3", "8.4.2", "8.4.1"]) | |
, (Shown, "8.2", "8.2.x", containsAny ["8.2.1"]) | |
, (Shown, "8.0", "8.0.x", containsAny ["8.0.2", "8.0.1"]) | |
, (Shown, "7.10", "7.10.x", containsAny ["7.10.3", "7.10.2", "7.10.1"]) | |
, (Shown, "7.8", "7.8.x", containsAny ["7.8.4", "7.8.3", "7.8.2", "7.8.1"]) | |
, (Hidden, "7.6", "7.6.x", containsAny ["7.6.2", "7.6.1"]) | |
, (Hidden, "7.4", "7.4.x", containsAny ["7.4.2", "7.4.1"]) | |
, (Hidden, "7.2", "7.2.x", containsAny ["7.2.2", "7.2.1"]) | |
, ( Hidden | |
, "7.0" | |
, "7.0.x" | |
, containsAny ["7.0.4", "7.0.3", "7.0.2", "7.0.1"] | |
) | |
] | |
makeSingleBar | |
responseQ36 | |
36 | |
"How do you feel about the new GHC release schedule?" | |
[ (Shown, "I like it", "I like it") | |
, (Shown, "I am indifferent", "I am indifferent") | |
, (Shown, "I dislike it", "I dislike it") | |
, (Shown, "I was not aware of it", "I was not aware of it") | |
] | |
makeMultipleBar | |
responseQ38 | |
38 | |
"Which GHC language extensions would you like to be enabled by default?" | |
[ (Shown, "OverloadedStrings", "OverloadedStrings") | |
, (Shown, "LambdaCase", "LambdaCase") | |
, (Shown, "DeriveFunctor", "DeriveFunctor") | |
, (Shown, "DeriveGeneric", "DeriveGeneric") | |
, (Shown, "BangPatterns", "BangPatterns") | |
, (Shown, "GADTs", "GADTs") | |
, (Hidden, "ScopedTypeVariables", "ScopedTypeVariables") | |
, (Hidden, "FlexibleInstances", "FlexibleInstances") | |
, (Hidden, "FlexibleContexts", "FlexibleContexts") | |
, (Hidden, "DeriveFoldable", "DeriveFoldable") | |
, (Hidden, "RankNTypes", "RankNTypes") | |
, (Hidden, "MultiParamTypeClasses", "MultiParamTypeClasses") | |
, (Hidden, "TupleSections", "TupleSections") | |
, (Hidden, "GeneralisedNewtypeDeriving", "GeneralisedNewtypeDeriving") | |
, (Hidden, "DeriveTraversable", "DeriveTraversable") | |
, (Hidden, "TypeApplications", "TypeApplications") | |
, (Hidden, "DataKinds", "DataKinds") | |
, (Hidden, "KindSignatures", "KindSignatures") | |
, (Hidden, "TypeOperators", "TypeOperators") | |
, (Hidden, "TypeFamilies", "TypeFamilies") | |
, (Hidden, "ApplicativeDo", "ApplicativeDo") | |
, (Hidden, "ViewPatterns", "ViewPatterns") | |
, (Hidden, "StandaloneDeriving", "StandaloneDeriving") | |
, (Hidden, "ConstraintKinds", "ConstraintKinds") | |
, (Hidden, "DerivingVia", "DerivingVia") | |
, (Hidden, "RecordWildCards", "RecordWildCards") | |
, (Hidden, "InstanceSigs", "InstanceSigs") | |
, (Hidden, "MultiWayIf", "MultiWayIf") | |
, (Hidden, "DeriveDataTypeable", "DeriveDataTypeable") | |
, (Hidden, "EmptyCase", "EmptyCase") | |
, (Hidden, "FunctionalDependencies", "FunctionalDependencies") | |
, (Hidden, "ExplicitForAll", "ExplicitForAll") | |
, (Hidden, "NamedFieldPuns", "NamedFieldPuns") | |
, (Hidden, "DerivingStrategies", "DerivingStrategies") | |
, (Hidden, "EmptyDataDecls", "EmptyDataDecls") | |
, (Hidden, "NumericUnderscores", "NumericUnderscores") | |
, (Hidden, "DeriveAnyClass", "DeriveAnyClass") | |
, (Hidden, "BinaryLiterals", "BinaryLiterals") | |
, (Hidden, "DefaultSignatures", "DefaultSignatures") | |
, (Hidden, "ExistentialQuantification", "ExistentialQuantification") | |
, (Hidden, "DuplicateRecordFields", "DuplicateRecordFields") | |
, (Hidden, "PatternSynonyms", "PatternSynonyms") | |
, (Hidden, "OverloadedLists", "OverloadedLists") | |
, (Hidden, "PolyKinds", "PolyKinds") | |
, (Hidden, "BlockArguments", "BlockArguments") | |
, (Hidden, "NoImplicitPrelude", "NoImplicitPrelude") | |
, (Hidden, "DeriveLift", "DeriveLift") | |
, (Hidden, "GADTSyntax", "GADTSyntax") | |
, (Hidden, "TypeSynonymInstances", "TypeSynonymInstances") | |
, (Hidden, "UnicodeSyntax", "UnicodeSyntax") | |
, (Hidden, "PatternGuards", "PatternGuards") | |
, (Hidden, "DisambiguateRecordFields", "DisambiguateRecordFields") | |
, (Hidden, "AutoDeriveTypeable", "AutoDeriveTypeable") | |
, (Hidden, "TypeInType", "TypeInType") | |
, (Hidden, "NoMonomorphismRestriction", "NoMonomorphismRestriction") | |
, (Hidden, "PartialTypeSignatures", "PartialTypeSignatures") | |
, (Hidden, "TypeFamilyDependencies", "TypeFamilyDependencies") | |
, (Hidden, "Arrows", "Arrows") | |
, (Hidden, "TemplateHaskell", "TemplateHaskell") | |
, (Hidden, "PackageImports", "PackageImports") | |
, (Hidden, "NamedWildCards", "NamedWildCards") | |
, (Hidden, "QuasiQuotes", "QuasiQuotes") | |
, (Hidden, "QuantifiedConstraints", "QuantifiedConstraints") | |
, (Hidden, "NegativeLiterals", "NegativeLiterals") | |
, (Hidden, "MonadComprehensions", "MonadComprehensions") | |
, (Hidden, "ParallelListComp", "ParallelListComp") | |
, (Hidden, "AllowAmbiguousTypes", "AllowAmbiguousTypes") | |
, (Hidden, "RecordPuns", "RecordPuns") | |
, (Hidden, "DoAndIfThenElse", "DoAndIfThenElse") | |
, (Hidden, "EmptyDataDeriving", "EmptyDataDeriving") | |
, (Hidden, "RecursiveDo", "RecursiveDo") | |
, (Hidden, "PatternSignatures", "PatternSignatures") | |
, (Hidden, "StarIsType", "StarIsType") | |
, (Hidden, "MagicHash", "MagicHash") | |
, (Hidden, "ForeignFunctionInterface", "ForeignFunctionInterface") | |
, (Hidden, "MonadFailDesugaring", "MonadFailDesugaring") | |
, (Hidden, "RoleAnnotations", "RoleAnnotations") | |
, (Hidden, "Rank2Types", "Rank2Types") | |
, (Hidden, "StrictData", "StrictData") | |
, (Hidden, "NumDecimals", "NumDecimals") | |
, (Hidden, "OverloadedLabels", "OverloadedLabels") | |
, (Hidden, "IncoherentInstances", "IncoherentInstances") | |
, (Hidden, "NullaryTypeClasses", "NullaryTypeClasses") | |
, (Hidden, "ImplicitParams", "ImplicitParams") | |
, (Hidden, "ConstrainedClassMethods", "ConstrainedClassMethods") | |
, (Hidden, "LiberalTypeSynonyms", "LiberalTypeSynonyms") | |
, (Hidden, "CPP", "CPP") | |
, (Hidden, "UndecidableInstances", "UndecidableInstances") | |
, (Hidden, "UnboxedTuples", "UnboxedTuples") | |
, (Hidden, "ExplicitNamespaces", "ExplicitNamespaces") | |
, (Hidden, "UnboxedSums", "UnboxedSums") | |
, (Hidden, "HexFloatLiterals", "HexFloatLiterals") | |
, (Hidden, "DoRec", "DoRec") | |
, (Hidden, "Strict", "Strict") | |
, (Hidden, "TransformListComp", "TransformListComp") | |
, (Hidden, "OverlappingInstances", "OverlappingInstances") | |
, (Hidden, "ParallelArrays", "ParallelArrays") | |
, (Hidden, "ExtendedDefaultRules", "ExtendedDefaultRules") | |
, (Hidden, "PostfixOperators", "PostfixOperators") | |
, (Hidden, "AlternativeLayoutRule", "AlternativeLayoutRule") | |
, (Hidden, "DatatypeContexts", "DatatypeContexts") | |
, (Hidden, "TemplateHaskellQuotes", "TemplateHaskellQuotes") | |
, (Hidden, "NPlusKPatterns", "NPlusKPatterns") | |
, (Hidden, "ImpredicativeTypes", "ImpredicativeTypes") | |
, (Hidden, "CApiFFI", "CApiFFI") | |
, (Hidden, "NoTraditionalRecordSyntax", "NoTraditionalRecordSyntax") | |
, (Hidden, "PolymorphicComponents", "PolymorphicComponents") | |
, (Hidden, "RebindableSyntax", "RebindableSyntax") | |
, (Hidden, "UnliftedFFITypes", "UnliftedFFITypes") | |
, ( Hidden | |
, "AlternativeLayoutRuleTransitional" | |
, "AlternativeLayoutRuleTransitional" | |
) | |
, (Hidden, "InterruptibleFFI", "InterruptibleFFI") | |
, (Hidden, "UndecidableSuperClasses", "UndecidableSuperClasses") | |
, (Hidden, "MonoLocalBinds", "MonoLocalBinds") | |
, (Hidden, "MonoPatBinds", "MonoPatBinds") | |
, (Hidden, "JavaScriptFFI", "JavaScriptFFI") | |
, (Hidden, "RelaxedPolyRec", "RelaxedPolyRec") | |
, (Hidden, "RelaxedLayout", "RelaxedLayout") | |
, (Hidden, "StaticPointers", "StaticPointers") | |
, (Hidden, "GHCForeignImportPrim", "GHCForeignImportPrim") | |
] | |
makeSingleBar | |
responseQ39 | |
39 | |
"How important do you feel it would be to have a new version of the Haskell standard?" | |
[ (Shown, "Not at all", "Not at all important") | |
, (Shown, "Slightly", "Slightly important") | |
, (Shown, "Moderately", "Moderately important") | |
, (Shown, "Very", "Very important") | |
, (Shown, "Extremely", "Extremely important") | |
] | |
makeRatingBar responseQ40 40 "Tooling" | |
makeMultipleBar | |
responseQ41 | |
41 | |
"Which build tools do you use for Haskell?" | |
[ (Shown, "Stack", "Stack") | |
, (Shown, "Cabal", "Cabal") | |
, (Shown, "Nix", "Nix") | |
, (Shown, "Make", "Make") | |
, (Shown, "Shake", "Shake") | |
, (Shown, "ghc-pkg", "ghc-pkg") | |
, (Shown, "Bazel", "Bazel") | |
, (Shown, "Mafia", "Mafia") | |
] | |
makeMultipleBar | |
responseQ43 | |
43 | |
"Which editors do you use for Haskell?" | |
[ (Shown, "Emacs", "Emacs") | |
, (Shown, "Vi", "Vi") | |
, (Shown, "VSCode", "Visual Studio Code") | |
, (Shown, "Atom", "Atom") | |
, (Shown, "Sublime Text", "Sublime Text") | |
, (Shown, "Notepad++", "Notepad++") | |
, (Shown, "IntelliJ IDEA", "IntelliJ IDEA") | |
] | |
makeMultipleBar | |
responseQ45 | |
45 | |
"Which version control systems do you use for Haskell?" | |
[ (Shown, "Git", "Git") | |
, (Shown, "Darcs", "Darcs") | |
, (Shown, "Mercurial", "Mercurial") | |
, (Shown, "Subversion", "Subversion") | |
, (Shown, "Pijul", "Pijul") | |
, (Shown, "Fossil", "Fossil") | |
] | |
makeMultipleBar | |
responseQ47 | |
47 | |
"Where do you get Haskell packages from?" | |
[ (Shown, "Stackage", "Stackage") | |
, (Shown, "Hackage", "Hackage") | |
, (Shown, "Source", "Source") | |
, (Shown, "Nix", "Nix") | |
] | |
makeMultipleBar | |
responseQ49 | |
49 | |
"Which libraries do you use to test Haskell code?" | |
[ (Shown, "QuickCheck", "QuickCheck") | |
, (Shown, "Hspec", "Hspec") | |
, (Shown, "Tasty", "Tasty") | |
, (Shown, "HUnit", "HUnit") | |
, (Shown, "Hedgehog", "Hedgehog") | |
, (Shown, "HTF", "Haskell Test Framework") | |
, (Shown, "SmallCheck", "SmallCheck") | |
, (Shown, "LeanCheck", "LeanCheck") | |
] | |
makeMultipleBar | |
responseQ51 | |
51 | |
"Which libraries do you use to benchmark Haskell code?" | |
[ (Shown, "Criterion", "Criterion") | |
, (Shown, "Bench", "Bench") | |
, (Shown, "Gauge", "Gauge") | |
] | |
makeRatingBar responseQ53 53 "Infrastructure" | |
makeMultipleBar | |
responseQ54 | |
54 | |
"Which tools do you use to deploy Haskell applications?" | |
[ (Shown, "Static binaries", "Static binaries") | |
, (Shown, "Docker images", "Docker images") | |
, (Shown, "Dynamic binaries", "Dynamic binaries") | |
, (Shown, "Nix expressions", "Nix expressions") | |
] | |
makeMultipleBar | |
responseQ56 | |
56 | |
"Where do you deploy Haskell applications?" | |
[ (Shown, "Own servers", "Self or company owned servers") | |
, (Shown, "AWS", "Amazon Web Services (AWS)") | |
, (Shown, "Google Cloud", "Google Cloud") | |
, (Shown, "Digital Ocean", "Digital Ocean") | |
, (Shown, "Heroku", "Heroku") | |
, (Shown, "Azure", "Microsoft Azure") | |
] | |
makeRatingBar responseQ58 58 "Community" | |
makeMultipleBar | |
responseQ59 | |
59 | |
"Where do you interact with the Haskell community?" | |
[ (Shown, "Reddit", "Reddit") | |
, (Shown, "GitHub", "GitHub") | |
, (Shown, "Twitter", "Twitter") | |
, (Shown, "IRC", "IRC") | |
, (Shown, "Mailing lists", "Mailing lists") | |
, (Shown, "Meetups", "Meetups") | |
, (Shown, "Stack Overflow", "Stack Overflow") | |
, (Shown, "Slack", "Slack") | |
, (Hidden, "Commerical conferences", "Conferences (commercial)") | |
, (Hidden, "Academic conferences", "Conferences (academic)") | |
, (Hidden, "Discord", "Discord") | |
, (Hidden, "Gitter", "Gitter") | |
, (Hidden, "Matrix/Riot", "Matrix/Riot") | |
, (Hidden, "Lobsters", "Lobsters") | |
, (Hidden, "Telegram", "Telegram") | |
, (Hidden, "Mastodon", "Mastodon") | |
] | |
makeMultipleBar | |
responseQ61 | |
61 | |
"Which of the following Haskell topics would you like to see more written about?" | |
[ (Shown, "Best practices", "Best practices") | |
, (Shown, "Patterns", "Design patterns") | |
, (Shown, "Architecture", "Application architectures") | |
, (Shown, "Performance", "Performance analysis") | |
, (Shown, "Libraries", "Library walkthroughs") | |
, (Shown, "Tooling", "Tooling choices") | |
, (Shown, "Debugging", "Debugging how-tos") | |
, (Shown, "Infrastructure", "Production infrastructure") | |
, (Hidden, "Case studies", "Case studies") | |
, (Hidden, "Web development", "Web development") | |
, (Hidden, "Algorithm implementations", "Algorithm implementations") | |
, (Hidden, "GUIs", "GUIs") | |
, (Hidden, "Beginner fundamentals", "Beginner fundamentals") | |
, (Hidden, "Machine learning", "Machine learning") | |
, (Hidden, "Project setup", "Project setup") | |
, (Hidden, "Project maintenance", "Project maintenance") | |
, (Hidden, "Game development", "Game development") | |
, (Hidden, "Mobile development", "Mobile development") | |
, ( Hidden | |
, "Comparisons to other languages" | |
, "Comparisons to other languages" | |
) | |
] | |
makeRatingBar responseQ63 63 "Feelings" | |
makeLikertBar responseQ64 64 "I feel welcome in the Haskell community." | |
makeLikertBar responseQ65 65 "I am satisfied with Haskell as a language." | |
makeLikertBar | |
responseQ67 | |
67 | |
"I am satisfied with Haskell's compilers, such as GHC." | |
makeLikertBar | |
responseQ69 | |
69 | |
"I am satisfied with Haskell's build tools, such as Cabal." | |
makeLikertBar | |
responseQ71 | |
71 | |
"I am satisfied with Haskell's package repositories, such as Hackage." | |
makeLikertBar | |
responseQ73 | |
73 | |
"I can find Haskell libraries for the things that I need." | |
makeLikertBar responseQ74 74 "I think Haskell libraries are high quality." | |
makeLikertBar | |
responseQ75 | |
75 | |
"I have a good understanding of Haskell best practices." | |
makeLikertBar responseQ76 76 "I think Haskell libraries are well documented." | |
makeLikertBar | |
responseQ77 | |
77 | |
"I can easily compare competing Haskell libraries to select the best one." | |
makeLikertBar | |
responseQ78 | |
78 | |
"I think that Haskell libraries are easy to use." | |
makeLikertBar | |
responseQ79 | |
79 | |
"I think that Haskell libraries provide a stable API." | |
makeLikertBar | |
responseQ80 | |
80 | |
"I think that Haskell libraries work well together." | |
makeLikertBar | |
responseQ81 | |
81 | |
"I think that software written in Haskell is easy to maintain." | |
makeLikertBar | |
responseQ82 | |
82 | |
"Once my Haskell program compiles, it generally does what I intended." | |
makeLikertBar responseQ83 83 "I think that Haskell libraries perform well." | |
makeLikertBar responseQ84 84 "Haskell's performance meets my needs." | |
makeLikertBar | |
responseQ85 | |
85 | |
"I can easily reason about the performance of my Haskell code." | |
makeLikertBar responseQ86 86 "I would recommend using Haskell to others." | |
makeLikertBar | |
responseQ87 | |
87 | |
"I would prefer to use Haskell for my next new project." | |
makeLikertBar responseQ88 88 "Haskell is working well for my team." | |
makeLikertBar responseQ89 89 "Haskell is critical to my company's success." | |
makeLikertBar | |
responseQ90 | |
90 | |
"As a candidate, I can easily find Haskell jobs." | |
makeLikertBar | |
responseQ91 | |
91 | |
"As a hiring manager, I can easily find qualified Haskell candidates." | |
makeRatingBar responseQ94 94 "Demographics" | |
makeSingleBar responseQ95 95 "Which country do you live in?" $ fmap | |
(\(visibility, country) -> (visibility, country, country)) | |
[ (Shown, "United States") | |
, (Shown, "United Kingdom") | |
, (Shown, "Germany") | |
, (Shown, "Australia") | |
, (Shown, "France") | |
, (Hidden, "Russia") | |
, (Hidden, "Canada") | |
, (Hidden, "Sweden") | |
, (Hidden, "Netherlands") | |
, (Hidden, "India") | |
, (Hidden, "Japan") | |
, (Hidden, "Italy") | |
, (Hidden, "Finland") | |
, (Hidden, "Poland") | |
, (Hidden, "Switzerland") | |
, (Hidden, "Austria") | |
, (Hidden, "Norway") | |
, (Hidden, "Czechia") | |
, (Hidden, "Denmark") | |
, (Hidden, "Belgium") | |
, (Hidden, "Spain") | |
, (Hidden, "New Zealand") | |
, (Hidden, "Brazil") | |
, (Hidden, "China") | |
, (Hidden, "Ireland") | |
, (Hidden, "Israel") | |
, (Hidden, "Ukraine") | |
, (Hidden, "Portugal") | |
, (Hidden, "Croatia") | |
, (Hidden, "Belarus") | |
, (Hidden, "Romania") | |
, (Hidden, "Argentina") | |
, (Hidden, "Bulgaria") | |
, (Hidden, "Greece") | |
, (Hidden, "Indonesia") | |
, (Hidden, "Latvia") | |
, (Hidden, "South Africa") | |
, (Hidden, "Turkey") | |
, (Hidden, "Ecuador") | |
, (Hidden, "Estonia") | |
, (Hidden, "Hungary") | |
, (Hidden, "Kenya") | |
, (Hidden, "Singapore") | |
, (Hidden, "Mexico") | |
, (Hidden, "Thailand") | |
, (Hidden, "Algeria") | |
, (Hidden, "Armenia") | |
, (Hidden, "Azerbaijan") | |
, (Hidden, "Bolivia") | |
, (Hidden, "Cameroon") | |
, (Hidden, "Chile") | |
, (Hidden, "Colombia") | |
, (Hidden, "Egypt") | |
, (Hidden, "Guatemala") | |
, (Hidden, "Iceland") | |
, (Hidden, "Iran") | |
, (Hidden, "Kazakhstan") | |
, (Hidden, "Lebanon") | |
, (Hidden, "Malta") | |
, (Hidden, "Nepal") | |
, (Hidden, "Nigeria") | |
, (Hidden, "Pakistan") | |
, (Hidden, "Paraguay") | |
, (Hidden, "Peru") | |
, (Hidden, "Philippines") | |
, (Hidden, "Serbia") | |
, (Hidden, "Slovakia") | |
, (Hidden, "Slovenia") | |
, (Hidden, "South Korea") | |
, (Hidden, "Syria") | |
, (Hidden, "Uganda") | |
, (Hidden, "Vietnam") | |
] | |
makeSingleBar | |
responseQ96 | |
96 | |
"How old are you?" | |
[ (Shown, "<18", "Under 18 years old") | |
, (Shown, "18-24", "18 to 24 years old") | |
, (Shown, "25-34", "25 to 34 years old") | |
, (Shown, "35-44", "35 to 44 years old") | |
, (Shown, "45-54", "45 to 54 years old") | |
, (Shown, "55-64", "55 to 64 years old") | |
, (Shown, ">=65", "65 years or older") | |
] | |
makeSingleBar | |
responseQ97 | |
97 | |
"What is your gender?" | |
[ (Shown, "Male", "Male") | |
, (Shown, "Female", "Female") | |
, (Shown, "Non-binary", "Non-binary") | |
] | |
makeSingleBar | |
responseQ98 | |
98 | |
"Do you identify as transgender?" | |
[(Shown, "No", "No"), (Shown, "Yes", "Yes")] | |
makeSingleBar | |
responseQ99 | |
99 | |
"Are you a student?" | |
[ (Shown, "No", "No") | |
, (Shown, "Yes, full time", "Yes, full time") | |
, (Shown, "Yes, part time", "Yes, part time") | |
] | |
makeSingleBar | |
responseQ100 | |
100 | |
"What is the highest level of education you have completed?" | |
[ ( Hidden | |
, "Less than high school diploma" | |
, "Less than high school diploma" | |
) | |
, (Shown, "High school", "High school diploma") | |
, (Shown, "Some college", "Some college") | |
, (Shown, "Associate", "Associate degree") | |
, (Shown, "Bachelor's", "Bachelor's degree") | |
, (Shown, "Master's", "Master's degree") | |
, (Shown, "Professional", "Professional degree") | |
, (Shown, "Doctoral", "Doctoral degree") | |
] | |
makeSingleBar | |
responseQ101 | |
101 | |
"What is your employment status?" | |
[ (Shown, "Full time", "Employed full time") | |
, (Shown, "Self employed", "Self employed") | |
, (Shown, "Part time", "Employed part time") | |
, (Shown, "Looking", "Not employed, but looking for work") | |
, (Shown, "Not looking", "Not employed, and not looking for work") | |
, (Shown, "Retired", "Retired") | |
] | |
makeSingleBar | |
responseQ102 | |
102 | |
"How large is the company you work for?" | |
[ (Shown, "<10", "Fewer than 10 employees") | |
, (Shown, "10-99", "10 to 99 employees") | |
, (Shown, "100-999", "100 to 999 employees") | |
, (Shown, ">=1000", "1,000 or more employees") | |
] | |
makeSingleBar | |
responseQ103 | |
103 | |
"How many years have you been coding?" | |
[ (Shown, "<5", "0 to 4 years") | |
, (Shown, "5-9", "5 to 9 years") | |
, (Shown, "10-14", "10 to 14 years") | |
, (Shown, "15-19", "15 to 19 years") | |
, (Shown, "20-24", "20 to 24 years") | |
, (Shown, "25-29", "25 to 29 years") | |
, (Shown, ">=30", "30 or more years") | |
] | |
makeSingleBar | |
responseQ104 | |
104 | |
"How many years have you been coding professionally?" | |
[ (Shown, "<5", "0 to 4 years") | |
, (Shown, "5-9", "5 to 9 years") | |
, (Shown, "10-19", "10 to 19 years") | |
, (Shown, "20-29", "20 to 29 years") | |
, (Shown, ">=30", "30 or more years") | |
] | |
makeSingleBar | |
responseQ105 | |
105 | |
"Do you code as a hobby?" | |
[(Shown, "Yes", "Yes"), (Shown, "No", "No")] | |
makeSingleBar | |
responseQ106 | |
106 | |
"Have you contributed to any open source projects?" | |
[(Shown, "Yes", "Yes"), (Shown, "No", "No")] | |
makeRatingBar responseQ107 107 "Meta survey" | |
makeSingleBar | |
responseQ108 | |
108 | |
"Did you take last year's survey?" | |
[ (Shown, "No", "No") | |
, (Shown, "Yes", "Yes") | |
, (Shown, "Not sure", "I don't remember") | |
] | |
makeSingleBar | |
responseQ109 | |
109 | |
"How did you hear about this survey?" | |
[ (Shown, "Reddit", "Reddit") | |
, (Shown, "Twitter", "Twitter") | |
, (Shown, "Haskell Weekly", "Haskell Weekly") | |
, (Shown, "Mailing list", "Mailing list") | |
, (Shown, "Other", "Other") | |
, (Shown, "In person", "In person") | |
, (Shown, "Lobsters", "Lobsters") | |
, (Shown, "Slack", "Slack") | |
, (Hidden, "Discord", "Discord") | |
, (Hidden, "IRC", "IRC") | |
, (Hidden, "Matrix/Riot", "Matrix/Riot") | |
, (Hidden, "Telegram", "Telegram") | |
, (Hidden, "Gitter", "Gitter") | |
, (Hidden, "Mastodon", "Mastodon") | |
] | |
IO.hPutStrLn handle "</body>" | |
IO.hPutStrLn handle "</html>" | |
IO.hFlush handle | |
data Response = Response | |
{ responseQ1 :: Date | |
-- ^ Submission date | |
, responseQ2 :: Maybe Int | |
-- ^ HASKELL USAGE | |
, responseQ3 :: Maybe Text.Text | |
-- ^ Do you use Haskell? | |
, responseQ4 :: Maybe Text.Text | |
-- ^ If you stopped using Haskell, how long did you use it before you stopped? | |
, responseQ5 :: Multiple Text.Text | |
-- ^ If you do not use Haskell, why not? | |
, responseQ7 :: Maybe Text.Text | |
-- ^ How long have you been using Haskell? | |
, responseQ8 :: Maybe Text.Text | |
-- ^ How frequently do you use Haskell? | |
, responseQ9 :: Maybe Text.Text | |
-- ^ How would you rate your proficiency in Haskell? | |
, responseQ10 :: Multiple Text.Text | |
-- ^ Where do you use Haskell? | |
, responseQ11 :: Maybe Text.Text | |
-- ^ Do you use Haskell at work? | |
, responseQ12 :: Multiple Text.Text | |
-- ^ If you do not use Haskell at work, why not? | |
, responseQ14 :: Multiple Text.Text | |
-- ^ Which programming languages other than Haskell are you fluent in? | |
, responseQ16 :: Multiple Text.Text | |
-- ^ Which types of software do you develop with Haskell? | |
, responseQ18 :: Multiple Text.Text | |
-- ^ Which industries do you use Haskell in? | |
, responseQ20 :: Maybe Int | |
-- ^ PROJECTS | |
, responseQ21 :: Maybe Text.Text | |
-- ^ How many Haskell projects do you contribute to? | |
, responseQ22 :: Maybe Text.Text | |
-- ^ What is the total size of all the Haskell projects you contribute to? | |
, responseQ23 :: Multiple Text.Text | |
-- ^ Which platforms do you develop Haskell on? | |
, responseQ25 :: Multiple Text.Text | |
-- ^ Which platforms do you target? | |
, responseQ27 :: Maybe Int | |
-- ^ COMPILERS | |
, responseQ28 :: Multiple Text.Text | |
-- ^ Which Haskell compilers do you use? | |
, responseQ30 :: Multiple Text.Text | |
-- ^ Which installation methods do you use for your Haskell compiler? | |
, responseQ32 :: Maybe Text.Text | |
-- ^ Has upgrading your Haskell compiler broken your code in the last year? | |
, responseQ33 :: Multiple Text.Text | |
-- ^ How has upgrading your Haskell compiler broken your code in the past year? | |
, responseQ35 :: Multiple Text.Text | |
-- ^ Which versions of GHC do you use? | |
, responseQ36 :: Maybe Text.Text | |
-- ^ How do you feel about the new GHC release schedule? | |
, responseQ38 :: Multiple Text.Text | |
-- ^ Which GHC language extensions would you like to be enabled by default? | |
, responseQ39 :: Maybe Text.Text | |
-- ^ How important do you feel it would be to have a new version of the Haskell standard? | |
, responseQ40 :: Maybe Int | |
-- ^ TOOLING | |
, responseQ41 :: Multiple Text.Text | |
-- ^ Which build tools do you use for Haskell? | |
, responseQ43 :: Multiple Text.Text | |
-- ^ Which editors do you use for Haskell? | |
, responseQ45 :: Multiple Text.Text | |
-- ^ Which version control systems do you use for Haskell? | |
, responseQ47 :: Multiple Text.Text | |
-- ^ Where do you get Haskell packages from? | |
, responseQ49 :: Multiple Text.Text | |
-- ^ Which libraries do you use to test Haskell code? | |
, responseQ51 :: Multiple Text.Text | |
-- ^ Which libraries do you use to benchmark Haskell code? | |
, responseQ53 :: Maybe Int | |
-- ^ INFRASTRUCTURE | |
, responseQ54 :: Multiple Text.Text | |
-- ^ Which tools do you use to deploy Haskell applications? | |
, responseQ56 :: Multiple Text.Text | |
-- ^ Where do you deploy Haskell applications? | |
, responseQ58 :: Maybe Int | |
-- ^ COMMUNITY | |
, responseQ59 :: Multiple Text.Text | |
-- ^ Where do you interact with the Haskell community? | |
, responseQ61 :: Multiple Text.Text | |
-- ^ Which of the following Haskell topics would you like to see more written about? | |
, responseQ63 :: Maybe Int | |
-- ^ FEELINGS | |
, responseQ64 :: Maybe Text.Text | |
-- ^ I feel welcome in the Haskell community. | |
, responseQ65 :: Maybe Text.Text | |
-- ^ I am satisfied with Haskell as a language. | |
, responseQ67 :: Maybe Text.Text | |
-- ^ I am satisfied with Haskell's compilers, such as GHC. | |
, responseQ69 :: Maybe Text.Text | |
-- ^ I am satisfied with Haskell's build tools, such as Cabal. | |
, responseQ71 :: Maybe Text.Text | |
-- ^ I am satisfied with Haskell's package repositories, such as Hackage. | |
, responseQ73 :: Maybe Text.Text | |
-- ^ I can find Haskell libraries for the things that I need. | |
, responseQ74 :: Maybe Text.Text | |
-- ^ I think Haskell libraries are high quality. | |
, responseQ75 :: Maybe Text.Text | |
-- ^ I have a good understanding of Haskell best practices. | |
, responseQ76 :: Maybe Text.Text | |
-- ^ I think Haskell libraries are well documented. | |
, responseQ77 :: Maybe Text.Text | |
-- ^ I can easily compare competing Haskell libraries to select the best one. | |
, responseQ78 :: Maybe Text.Text | |
-- ^ I think that Haskell libraries are easy to use. | |
, responseQ79 :: Maybe Text.Text | |
-- ^ I think that Haskell libraries provide a stable API. | |
, responseQ80 :: Maybe Text.Text | |
-- ^ I think that Haskell libraries work well together. | |
, responseQ81 :: Maybe Text.Text | |
-- ^ I think that software written in Haskell is easy to maintain. | |
, responseQ82 :: Maybe Text.Text | |
-- ^ Once my Haskell program compiles, it generally does what I intended. | |
, responseQ83 :: Maybe Text.Text | |
-- ^ I think that Haskell libraries perform well. | |
, responseQ84 :: Maybe Text.Text | |
-- ^ Haskell's performance meets my needs. | |
, responseQ85 :: Maybe Text.Text | |
-- ^ I can easily reason about the performance of my Haskell code. | |
, responseQ86 :: Maybe Text.Text | |
-- ^ I would recommend using Haskell to others. | |
, responseQ87 :: Maybe Text.Text | |
-- ^ I would prefer to use Haskell for my next new project. | |
, responseQ88 :: Maybe Text.Text | |
-- ^ Haskell is working well for my team. | |
, responseQ89 :: Maybe Text.Text | |
-- ^ Haskell is critical to my company's success. | |
, responseQ90 :: Maybe Text.Text | |
-- ^ As a candidate, I can easily find Haskell jobs. | |
, responseQ91 :: Maybe Text.Text | |
-- ^ As a hiring manager, I can easily find qualified Haskell candidates. | |
, responseQ94 :: Maybe Int | |
-- ^ DEMOGRAPHICS | |
, responseQ95 :: Maybe Text.Text | |
-- ^ Which country do you live in? | |
, responseQ96 :: Maybe Text.Text | |
-- ^ How old are you? | |
, responseQ97 :: Maybe Text.Text | |
-- ^ What is your gender? | |
, responseQ98 :: Maybe Text.Text | |
-- ^ Do you identify as transgender? | |
, responseQ99 :: Maybe Text.Text | |
-- ^ Are you a student? | |
, responseQ100 :: Maybe Text.Text | |
-- ^ What is the highest level of education you have completed? | |
, responseQ101 :: Maybe Text.Text | |
-- ^ What is your employment status? | |
, responseQ102 :: Maybe Text.Text | |
-- ^ How large is the company you work for? | |
, responseQ103 :: Maybe Text.Text | |
-- ^ How many years have you been coding? | |
, responseQ104 :: Maybe Text.Text | |
-- ^ How many years have you been coding professionally? | |
, responseQ105 :: Maybe Text.Text | |
-- ^ Do you code as a hobby? | |
, responseQ106 :: Maybe Text.Text | |
-- ^ Have you contributed to any open source projects? | |
, responseQ107 :: Maybe Int | |
-- ^ META SURVEY | |
, responseQ108 :: Maybe Text.Text | |
-- ^ Did you take last year's survey? | |
, responseQ109 :: Maybe Text.Text | |
-- ^ How did you hear about this survey? | |
, responseQ110 :: Maybe Text.Text | |
-- ^ Do you have any feedback about the survey itself? | |
} | |
instance Csv.FromRecord Response where | |
parseRecord record = Response | |
<$> Csv.index record 1 | |
<*> Csv.index record 2 | |
<*> Csv.index record 3 | |
<*> Csv.index record 4 | |
<*> Csv.index record 5 | |
<*> Csv.index record 7 | |
<*> Csv.index record 8 | |
<*> Csv.index record 9 | |
<*> Csv.index record 10 | |
<*> Csv.index record 11 | |
<*> Csv.index record 12 | |
<*> Csv.index record 14 | |
<*> Csv.index record 16 | |
<*> Csv.index record 18 | |
<*> Csv.index record 20 | |
<*> Csv.index record 21 | |
<*> Csv.index record 22 | |
<*> Csv.index record 23 | |
<*> Csv.index record 25 | |
<*> Csv.index record 27 | |
<*> Csv.index record 28 | |
<*> Csv.index record 30 | |
<*> Csv.index record 32 | |
<*> Csv.index record 33 | |
<*> Csv.index record 35 | |
<*> Csv.index record 36 | |
<*> Csv.index record 38 | |
<*> Csv.index record 39 | |
<*> Csv.index record 40 | |
<*> Csv.index record 41 | |
<*> Csv.index record 43 | |
<*> Csv.index record 45 | |
<*> Csv.index record 47 | |
<*> Csv.index record 49 | |
<*> Csv.index record 51 | |
<*> Csv.index record 53 | |
<*> Csv.index record 54 | |
<*> Csv.index record 56 | |
<*> Csv.index record 58 | |
<*> Csv.index record 59 | |
<*> Csv.index record 61 | |
<*> Csv.index record 63 | |
<*> Csv.index record 64 | |
<*> Csv.index record 65 | |
<*> Csv.index record 67 | |
<*> Csv.index record 69 | |
<*> Csv.index record 71 | |
<*> Csv.index record 73 | |
<*> Csv.index record 74 | |
<*> Csv.index record 75 | |
<*> Csv.index record 76 | |
<*> Csv.index record 77 | |
<*> Csv.index record 78 | |
<*> Csv.index record 79 | |
<*> Csv.index record 80 | |
<*> Csv.index record 81 | |
<*> Csv.index record 82 | |
<*> Csv.index record 83 | |
<*> Csv.index record 84 | |
<*> Csv.index record 85 | |
<*> Csv.index record 86 | |
<*> Csv.index record 87 | |
<*> Csv.index record 88 | |
<*> Csv.index record 89 | |
<*> Csv.index record 90 | |
<*> Csv.index record 91 | |
<*> Csv.index record 94 | |
<*> Csv.index record 95 | |
<*> Csv.index record 96 | |
<*> Csv.index record 97 | |
<*> Csv.index record 98 | |
<*> Csv.index record 99 | |
<*> Csv.index record 100 | |
<*> Csv.index record 101 | |
<*> Csv.index record 102 | |
<*> Csv.index record 103 | |
<*> Csv.index record 104 | |
<*> Csv.index record 105 | |
<*> Csv.index record 106 | |
<*> Csv.index record 107 | |
<*> Csv.index record 108 | |
<*> Csv.index record 109 | |
<*> Csv.index record 110 | |
newtype Date | |
= Date Time.Day | |
instance Csv.FromField Date where | |
parseField field = do | |
string <- Csv.parseField field | |
Date <$> Time.parseTimeM False Time.defaultTimeLocale "%Y-%m-%d" string | |
dateToDay :: Date -> Time.Day | |
dateToDay (Date day) = day | |
newtype Multiple a | |
= Multiple (Set.Set a) | |
instance (Csv.FromField a, Ord a) => Csv.FromField (Multiple a) where | |
parseField field = do | |
records <- | |
either fail pure . Csv.decode Csv.NoHeader $ LazyByteString.fromStrict | |
field | |
record <- case Vector.toList records of | |
[] -> pure Vector.empty | |
[record] -> pure record | |
_ -> fail $ "too many records: " ++ show records | |
Multiple . Set.fromList . Vector.toList <$> traverse Csv.parseField record | |
multipleToSet :: Multiple a -> Set.Set a | |
multipleToSet (Multiple set) = set | |
data Visibility | |
= Hidden | |
| Shown | |
contains :: String -> Multiple Text.Text -> Bool | |
contains = containsAny . pure | |
containsAny :: [String] -> Multiple Text.Text -> Bool | |
containsAny xs ys = any (\x -> Set.member (Text.pack x) (multipleToSet ys)) xs | |
count :: (a -> Bool) -> Vector.Vector a -> Int | |
count predicate = Vector.foldr | |
(\element total -> if predicate element then total + 1 else total) | |
0 | |
escape :: String -> String | |
escape = concatMap escapeChar | |
escapeChar :: Char -> String | |
escapeChar c = case c of | |
'\'' -> "'" | |
'"' -> """ | |
'&' -> "&" | |
'<' -> "<" | |
'>' -> ">" | |
_ -> [c] | |
is :: String -> Maybe Text.Text -> Bool | |
is string field = field == Just (Text.pack string) | |
makeBarWith | |
:: IO.Handle | |
-> Diagrams.FileOptions | |
-> Vector.Vector Response | |
-> (Response -> a) | |
-> Int | |
-> String | |
-> [(Visibility, String, String, a -> Bool)] | |
-> IO () | |
makeBarWith handle options responses getField number title bins = do | |
let | |
path = FilePath.combine outputDirectory | |
$ FilePath.addExtension (Printf.printf "question-%03d" number) "svg" | |
fields = fmap getField responses | |
bars = fmap (\(v, s, l, p) -> (v, s, l, count p fields)) bins | |
Printf.printf "- [%s](#question-%03d)\n" title number | |
let total = fromIntegral $ Vector.length responses :: Double | |
IO.hPutStr handle $ Printf.printf "<h2 id='question-%03d'>" number | |
IO.hPutStr handle $ Printf.printf "<a href='#question-%03d'>#</a> " number | |
IO.hPutStr handle $ escape title | |
IO.hPutStr handle " <a href='#'>^</a>" | |
IO.hPutStrLn handle "</h2>" | |
IO.hPutStr handle $ Printf.printf "<a href='question-%03d.svg'>" number | |
IO.hPutStr handle | |
. uncurry | |
(Printf.printf | |
"<img src='question-%03d.svg' alt='' width='%f' height='%f'>" | |
number | |
) | |
$ Chart.view Diagrams.fo_size options | |
IO.hPutStrLn handle "</a>" | |
IO.hPutStrLn handle "<table>" | |
IO.hPutStrLn handle "<thead>" | |
IO.hPutStr handle "<tr>" | |
IO.hPutStr handle "<th>Answer</th>" | |
IO.hPutStr handle "<th>Count</th>" | |
IO.hPutStr handle "<th>Percent</th>" | |
IO.hPutStr handle "</tr>" | |
IO.hPutStrLn handle "</thead>" | |
IO.hPutStrLn handle "<tbody>" | |
Monad.forM_ bars $ \(_, _, l, c) -> do | |
IO.hPutStr handle "<tr>" | |
IO.hPutStr handle . Printf.printf "<td>%s</td>" $ escape l | |
IO.hPutStr handle $ Printf.printf "<td>%d</td>" c | |
IO.hPutStr handle | |
. Printf.printf "<td>%.1f%%</td>" | |
$ 100 | |
* fromIntegral c | |
/ total | |
IO.hPutStrLn handle "</tr>" | |
IO.hPutStrLn handle "</tbody>" | |
IO.hPutStrLn handle "</table>" | |
Diagrams.toFile options path $ do | |
Chart.assign Chart.layout_title title | |
Chart.setColors . pure . Chart.opaque $ Color.sRGB24 0x5c 0x35 0x66 | |
Chart.assign (Chart.layout_x_axis . Chart.laxis_generate) | |
. Chart.autoIndexAxis | |
$ Maybe.mapMaybe | |
(\(v, s, _, _) -> case v of | |
Hidden -> Nothing | |
Shown -> Just s | |
) | |
bars | |
Chart.plot | |
. fmap Chart.plotBars | |
. Chart.bars [""] | |
. Chart.addIndexes | |
. fmap pure | |
$ Maybe.mapMaybe | |
(\(v, _, _, c) -> case v of | |
Hidden -> Nothing | |
Shown -> Just c | |
) | |
bars | |
makeLikertBarWith | |
:: IO.Handle | |
-> Diagrams.FileOptions | |
-> Vector.Vector Response | |
-> (Response -> Maybe Text.Text) | |
-> Int | |
-> String | |
-> IO () | |
makeLikertBarWith handle options responses getField number title = | |
makeBarWith handle options responses getField number title $ fmap | |
(\x -> (Shown, x, x, is x)) | |
["Strongly disagree", "Disagree", "Neutral", "Agree", "Strongly agree"] | |
makeMultipleBarWith | |
:: IO.Handle | |
-> Diagrams.FileOptions | |
-> Vector.Vector Response | |
-> (Response -> Multiple Text.Text) | |
-> Int | |
-> String | |
-> [(Visibility, String, String)] | |
-> IO () | |
makeMultipleBarWith handle options responses getField number title = | |
makeBarWith | |
handle | |
options | |
responses | |
getField | |
number | |
(Printf.printf "%s (multiple select)" title) | |
. fmap | |
(\(visibility, name, value) -> | |
(visibility, name, value, contains value) | |
) | |
makeRatingBarWith | |
:: IO.Handle | |
-> Diagrams.FileOptions | |
-> Vector.Vector Response | |
-> (Response -> Maybe Int) | |
-> Int | |
-> String | |
-> IO () | |
makeRatingBarWith handle options responses getField number title = | |
makeBarWith handle options responses getField number title $ fmap | |
(\n -> | |
( Shown | |
, show n | |
, Printf.printf "%d heart%s" n (if n == 1 then "" else "s") | |
, (== Just n) | |
) | |
) | |
[1 .. 10] | |
makeSingleBarWith | |
:: IO.Handle | |
-> Diagrams.FileOptions | |
-> Vector.Vector Response | |
-> (Response -> Maybe Text.Text) | |
-> Int | |
-> String | |
-> [(Visibility, String, String)] | |
-> IO () | |
makeSingleBarWith handle options responses getField number title = | |
makeBarWith | |
handle | |
options | |
responses | |
getField | |
number | |
(Printf.printf "%s (single select)" title) | |
. fmap (\(visibility, name, value) -> (visibility, name, value, is value)) | |
outputDirectory :: FilePath | |
outputDirectory = "output" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment