| name | haskell |
| description | Expert Haskell development assistance. Use when working with Haskell code, .hs files, Cabal, ghcid, or when user mentions Haskell, functional programming, or type-level programming. |
Haskell Development
Expert assistance for Haskell programming.
Guidelines
CRITICAL - Error Handling in Code: NEVER write code that silently ignores errors:
- Do NOT use
undefinedorerroras placeholders - Do NOT skip handling error cases in pattern matches
- Do NOT ignore
Maybe/Eitherfailure cases - Handle all possible cases explicitly
- Use types to make impossible states unrepresentable
Every error case in generated code must be handled properly.
Code Quality:
- Write type signatures for all top-level definitions
- Write total functions (avoid
head,tail) - Prefer pure functions over IO when possible
- Use explicit exports in modules
- Leverage type system for safety
- Favor composition over complex functions
- Write Haddock documentation for public APIs
Idiomatic Patterns:
- Prefer
TextoverString - Use
newtypewrappers for domain types - Apply smart constructors for validation
- Use RecordDotSyntax (
value.field) instead of manually unpacking data types - Use lenses for record manipulation when appropriate
- Use
ApplicativeandMonadappropriately - Avoid trivial
letbindings when inlining keeps code simple and readable
HLint
IMPORTANT: Always check if the project has a .hlint.yaml file. If it does:
- Read the
.hlint.yamlfile to understand project-specific style rules - Follow all recommendations in that file when writing code
- After making code changes, run
hlintto ensure no warnings
Relude Best Practices
When using relude prelude, follow these HLint recommendations (from https://github.com/kowainik/relude/blob/main/.hlint.yaml):
Basic Idioms:
- Use
passinstead ofpure ()orreturn () - Use
oneinstead of(: []),(:| []), or singleton functions - Use
<<$>>for double fmap:f <<$>> xinstead offmap (fmap f) x - Use
??(flap) operator:ff ?? xinstead offmap ($ x) ff
File I/O:
readFileText,writeFileText,appendFileTextfor Text filesreadFileLText,writeFileLText,appendFileLTextfor lazy TextreadFileBS,writeFileBS,appendFileBSfor ByteStringreadFileLBS,writeFileLBS,appendFileLBSfor lazy ByteString
Console Output:
putText,putTextLnfor TextputLText,putLTextLnfor lazy TextputBS,putBSLnfor ByteStringputLBS,putLBSLnfor lazy ByteString
Maybe/Either Helpers:
whenJust m finstead ofmaybe pass f mwhenJustM m ffor monadic versionswhenNothing_ m x/whenNothingM_ m xfor Nothing caseswhenLeft_ m f,whenRight_ m ffor EitherwhenLeftM_ m f,whenRightM_ m ffor monadic EitherleftToMaybe,rightToMaybefor conversionsmaybeToRight l,maybeToLeft rfor conversionsisLeft,isRightinstead ofeither (const True/False) (const False/True)
List Operations:
- Use
ordNubinstead ofnub(O(n log n) vs O(n²)) - Use
sortNubinstead ofData.Set.toList . Data.Set.fromList - Use
sortWith finstead ofsortBy (comparing f)for simple cases - Use
viaNonEmpty f xinstead offmap f (nonEmpty x) - Use
asumMap f xsinstead ofasum (map f xs) - Use
toListinstead offoldr (:) []
Monadic Operations:
andM sinstead ofand <$> sequence sorM sinstead ofor <$> sequence sallM f sinstead ofand <$> mapM f sanyM f sinstead ofor <$> mapM f sguardM finstead off >>= guardinfinitelyinstead offorever(better typed)unlessM (not <$> x)→ usewhenM xinsteadwhenM (not <$> x)→ useunlessM xinstead
State/Reader Operations:
usingReaderTinstead offlip runReaderTusingStateTinstead offlip runStateTevaluatingStateT s stinstead offst <$> usingStateT s stexecutingStateT s stinstead ofsnd <$> usingStateT s st
Transformer Lifting:
hoistMaybe minstead ofMaybeT (pure m)hoistEither minstead ofExceptT (pure m)
List Pattern Matching:
whenNotNull m fforcase m of [] -> pass; (x:xs) -> f (x :| xs)whenNotNullM m ffor monadic version
Text/ByteString Conversions:
- Use relude's
toText,toString,toLTextinstead of pack/unpack - Use relude's
encodeUtf8,decodeUtf8for UTF-8 encoding fromStrict,toStrictfor lazy/strict conversions
Testing
- Use QuickCheck for property-based testing
- Use HUnit or Hspec for unit tests
- Provide good examples in documentation
Build instructions
As you make code changes, start a subagent in parallel to resolve any compile errors in ghcid.txt.
IMPORTANT: Do not run build commands yourself. The human runs ghcid in the terminal, which then updates ghcid.txt with any compile error or warning (if this file does not exist, or if ghcid has stopped, remind the human to address it). You should read ghcid.txt (in entirety) after making code changes; this file updates near-instantly.
Adding/Deleting modules: When a new .hs file is added or deleted, the .cabal file must be updated accordingly. However, if package.yaml exists in the project, run hpack instead to regenerate the .cabal file with the updated module list. This will trigger ghcid to restart automatically.
HLint warnings: Once all code changes are made and ghcid.txt shows success, check if the project has a .hlint.yaml file. If it does, run hlint to ensure there are no warnings and address any that appear.