Profpatsch/.claude/CLAUDE-haskell.md

Code Style and Standards

cabal repl <package>          # Interactive development
cabal build <package>         # Build specific package
cabal test <package>          # Run tests for package

GHCi Capture Tool

Use the custom ghci-capture tool instead of raw tmux capture:

# Use relative path to the tool
./ghci-capture session-name

# Shows intelligent context - if last prompt is empty, shows previous command output
# Much better than raw tmux capture for GHCi sessions

Haskell Conventions (enforced by .hlint.yaml)

Core Philosophy: Optimize for clarity and safety over conciseness. Code should be explicit, readable, and resistant to runtime errors.

Safety First: Ban Partial Functions

Never use these dangerous functions - they crash on edge cases:

-- BAD: Partial functions that crash
head [1,2,3]          -- crashes on []
tail [1,2,3]          -- crashes on []
maximum [1,2,3]       -- crashes on []
minimum [1,2,3]       -- crashes on []
fromJust (Just 42)    -- crashes on Nothing
fromRight (Left err)  -- crashes on Left

-- GOOD: Explicit pattern matching
case xs of
  [] -> handleEmpty
  (x:rest) -> useHead x

case myMaybe of
  Nothing -> handleNothing  
  Just value -> useValue value

Use safe alternatives:

Modern Haskell: Preferred Functions

Use modern equivalents:

-- OLD           -> NEW
return x         -> pure x
mapM f xs        -> traverse f xs  
mapM_ f xs       -> traverse_ f xs
forM xs f        -> for xs f
forM_ xs f       -> for_ xs f
undefined        -> todo  -- shows warnings until removed

Explicit Over Clever

Prefer explicit code over point-free style:

-- BAD: Point-free style (hard to read)
f = map (+1) . filter even . concat

-- GOOD: Explicit pipeline  
f xs = xs
  & concat
  & filter even
  & map (+1)

-- BAD: const obscures intent
const True

-- GOOD: Explicit lambda with type information
\(_ :: Request) -> True

Avoid helper functions that hide logic:

-- BAD: fromMaybe hides the Nothing case
fromMaybe defaultValue myMaybe

-- GOOD: Explicit pattern matching
case myMaybe of
  Nothing -> defaultValue
  Just value -> value

Type-Safe Length and Containers

Use specific length functions:

-- BAD: Generic length is dangerous
length (3,4)        -- equals 1 (surprising!)
length (Just 42)    -- equals 1 (surprising!)

-- GOOD: Type-specific length
List.length [1,2,3]
Map.length myMap
Set.length mySet

Use strict containers:

-- BAD: Lazy containers cause space leaks
import Data.Map

-- GOOD: Strict containers
import Data.Map.Strict as Map
import Data.HashMap.Strict as HashMap

-- BAD: O(n²) nub
nub [1,2,1,3,2]

-- GOOD: O(n log n) nubOrd  
nubOrd [1,2,1,3,2]

Text and ByteString Handling

Use my-prelude conversion functions:

-- BAD: Raw encoding functions
Data.Text.Encoding.encodeUtf8
Data.Text.Encoding.decodeUtf8

-- GOOD: Explicit error handling variants
textToBytesUtf8           -- safe conversion
bytesToTextUtf8           -- safe with Either result
bytesToTextUtf8Lenient    -- replacement chars for invalid UTF-8
bytesToTextUtf8Unsafe     -- crashes on invalid UTF-8 (discouraged)

-- Lazy/strict conversions
toStrict      -- Text.Lazy -> Text
toLazy        -- Text -> Text.Lazy  
toStrictBytes -- ByteString.Lazy -> ByteString
toLazyBytes   -- ByteString -> ByteString.Lazy

Left-to-Right Pipeline Style

Prefer data flow left-to-right:

-- BAD: Backward composition (right-to-left reading)
result = f . g . h $ input

-- GOOD: Forward pipeline (left-to-right reading)
result = input
  & h
  & g  
  & f

-- GOOD: Bind chain for monadic operations
result <- input
  >>= h
  >>= g
  >>= f

Error Handling Patterns

Use Data.Error.Tree for structured errors:

-- GOOD: Structured error collection
parseUser :: Json.Value -> Either ErrorTree User
parseUser = Json.parse $ do
  name <- Json.key "name" Json.asText
  age <- Json.key "age" Json.asInt
  pure User{..}

-- GOOD: Error annotation
result <- computation
  & annotate "Failed to process user data"  
  & orAppThrow span

Module Import Guidelines

Use qualified imports for common modules:

import Data.Map.Strict as Map
import Data.HashMap.Strict as HashMap  
import Data.Set as Set
import Data.Text as Text
import Data.Aeson as Json
import Data.ByteString.Char8 as Char8

Testing Framework

Documentation Research with Hoogle

For comprehensive Haskell library documentation research:

Core Research Tools

# Function signatures and documentation
hoogle search --json "functionName"

# Module-level architecture and guidance  
hoogle search --json "Module.Name"

# Type definitions and constructors
hoogle search --json "TypeName"

Research Workflow Pattern

  1. Discover functions: Use grep to find what exists in documentation files

    grep -r "functionPattern" /nix/store/*-doc/share/doc/*/html/
    
  2. Get precise details: Use hoogle search --json for exact signatures and comprehensive documentation

  3. Cross-reference: Validate implementation approaches against library design patterns

Key Benefits

Documentation Sources

Alternative: Local Hoogle Server

For unrestricted documentation access with rich HTML content:

Setup:

# Start documentation server in tmux
tmux new-session -d -s hoogle-docs
tmux send-keys -t hoogle-docs "hoogle server --local --port 9090" Enter

# Verify server is running
sleep 3 && curl -s http://localhost:9090 | head -10

Research Commands:

# Search for modules - comprehensive architectural documentation
curl -s "http://localhost:9090/?hoogle=Network.HTTP.Client" | head -100

# Search for functions - exact signatures and usage examples  
curl -s "http://localhost:9090/?hoogle=setQueryString" | grep -A 20 -B 5 "setQueryString"

# Search for types - constructors and cross-package usage
curl -s "http://localhost:9090/?hoogle=defaultRequest" | grep -A 15 -B 5 "defaultRequest"

# Use byte ranges for large responses
curl -s -r 0-2047 "http://localhost:9090/?hoogle=searchTerm"

Key Benefits:

Proven Workflow: Start server → curl search → grep filter → validate with hoogle search --json

Research methodology: Always verify library functions via Hoogle before implementation to ensure accurate, idiomatic Haskell code.