用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tools-only/X-Skills --skill haskell-pro命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | haskell-pro |
| description | Expert in Haskell language and cabal module system. |
Advanced Haskell specialist combining deep functional programming expertise with production-grade engineering practices. Masters type-level programming, performance optimization, concurrent systems design, real-world application development with emphasis on correctness, composability, maintainability.
Advanced Type Features:
Type-Level Programming:
Essential Extensions:
BangPatterns, StrictData: Control evaluation strategyOverloadedStrings, OverloadedLists: Polymorphic literalsTypeApplications, AllowAmbiguousTypes: Explicit type passingScopedTypeVariables, ExplicitForAll: Type variable scopingDerivingStrategies, GeneralizedNewtypeDeriving: Deriving controlAdvanced Extensions:
DataKinds, PolyKinds: Promoted data types and kind polymorphismTypeFamilies, TypeFamilyDependencies: Type-level functionsConstraintKinds, FlexibleContexts: Constraint abstractionRankNTypes, ImpredicativeTypes: Higher-rank polymorphismQuantifiedConstraints: Constraints with forallViewPatterns, PatternSynonyms: Advanced pattern matchingRecordWildCards, NamedFieldPuns: Record syntax sugarFunctionalDependencies, UndecidableInstances: Type class designCore Abstractions:
Advanced Patterns:
MTL (Monad Transformer Library):
Alternative Effect Systems:
Code Generation:
Advanced TH Techniques:
Project Management:
Advanced Configuration:
flag dev
description: Development build
default: False
manual: True
common warnings
ghc-options: -Wall -Wcompat -Widentities
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wpartial-fields -Wredundant-constraints
library
import: warnings
if flag(dev)
ghc-options: -O0
else
ghc-options: -O2 -funbox-strict-fields
Haskell.nix:
Development Shells:
mkShell {
buildInputs = with haskellPackages; [
ghc
cabal-install
haskell-language-server
hlint
fourmolu
ghcid
];
}
Warning Sets:
-Wall -Wcompat for maximum compatibility-Weverything for exploration, then whitelistOptimization Strategies:
-O2 vs -O trade-offs-funbox-strict-fields for data types-fspecialise-aggressively for polymorphic code-flate-dmd-anal for better strictness analysis-fllvm for numerical code-fprof-autoLanguage Servers:
Code Quality Tools:
Space Leak Detection & Prevention:
Thunk Management:
-- Space leak
average xs = sum xs / fromIntegral (length xs)
-- Fixed with strict accumulator
average xs = uncurry (/) $ foldl' (\(!s,!c) x -> (s+x, c+1)) (0,0) xs
Memory Profiling Techniques:
+RTS -hT-hb) for lifecycle analysisPerformance Characteristics:
-- Lists: O(n) indexing, O(1) cons, lazy, good for streaming
-- Vectors: O(1) indexing, O(n) cons, strict, cache-friendly
-- Arrays: O(1) indexing, immutable, unboxed variants
-- Sequences: O(log n) everything, good general purpose
-- IntMap/Map: O(log n) operations, persistent
-- HashMap: O(1) average operations, requires Hashable
-- Set/IntSet: Unique elements, O(log n) operations
Specialized Structures:
Streaming Libraries Comparison:
Streaming Patterns:
-- Conduit example for large file processing
processLargeFile :: FilePath -> IO ()
processLargeFile path = runConduitRes $
sourceFile path
.| linesUnboundedAsciiC
.| mapC processLine
.| sinkFile output
Par Monad & Strategies:
-- Parallel map with strategies
parMap :: (a -> b) -> [a] -> [b]
parMap f xs = xs `using` parList rdeepseq
-- Parallel divide-and-conquer
parFold :: (a -> a -> a) -> [a] -> a
parFold f xs = fold `using` strategy
where
fold = treeReduce f xs
strategy = parTree 2
Data Parallel Arrays:
STM Patterns:
-- Bounded queue with STM
data TBQueue a = TBQueue
{ queue :: TVar (Seq a)
, size :: TVar Int
, maxSize :: Int
}
-- Work-stealing deque
data WSDeque a = WSDeque
{ top :: TVar [a]
, bottom :: TVar [a]
}
Async Patterns:
-- Concurrent map with bounded parallelism
mapConcurrentlyBounded :: Int -> (a -> IO b) -> [a] -> IO [b]
mapConcurrentlyBounded n f xs = do
sem <- newQSem n
asyncs <- forM xs $ \x ->
async $ bracket_ (waitQSem sem) (signalQSem sem) (f x)
mapM wait asyncs
Fusion & Deforestation:
Specialization:
{-# SPECIALIZE sumPoly :: [Int] -> Int #-}
{-# SPECIALIZE sumPoly :: [Double] -> Double #-}
sumPoly :: Num a => [a] -> a
sumPoly = foldl' (+) 0
Inlining Control:
{-# INLINE critical #-} -- Always inline
{-# INLINABLE flexible #-} -- Inline when beneficial
{-# NOINLINE stable #-} -- Never inline
type UserAPI = "users" :> Get '[JSON] [User]
:<|> "users" :> Capture "id" UserId :> Get '[JSON] User
:<|> "users" :> ReqBody '[JSON] NewUser :> Post '[JSON] User
-- Automatic client generation
userClient :: ClientM [User] :<|> (UserId -> ClientM User) :<|> (NewUser -> ClientM User)
userClient = client (Proxy :: Proxy UserAPI)
-- OpenAPI documentation generation
userDocs :: OpenApi
userDocs = toOpenApi (Proxy :: Proxy UserAPI)
app :: Application
app = requestLogger
$ gzip def
$ cors (const $ Just corsPolicy)
$ rateLimiting
$ myApp
-- Type-safe schema definition
share [mkPersist sqlSettings] [persistLowerCase|
User
name Text
email Text
UniqueEmail email
deriving Show
|]
-- Type-safe queries with Esqueleto
getUserPosts :: UserId -> SqlPersistT IO [(Entity User, Entity Post)]
getUserPosts uid =
select $ from $ \(user `InnerJoin` post) -> do
on (user ^. UserId ==. post ^. PostUserId)
where_ (user ^. UserId ==. val uid)
orderBy [desc (post ^. PostCreated)]
return (user, post)
userByEmail :: Statement Text (Maybe User)
userByEmail = Statement sql encoder decoder True
where
sql = "SELECT * FROM users WHERE email = $1"
encoder = Encoders.param (Encoders.nonNullable Encoders.text)
decoder = Decoders.rowMaybe userDecoder
-- Custom error messages
data CustomError = InvalidFormat String
deriving (Eq, Show, Ord)
type Parser = Parsec CustomError Text
-- Parser with good error messages
jsonValue :: Parser Value
jsonValue = label "JSON value" $
choice [ Object <$> object
, Array <$> array
, String <$> string
, Number <$> number
, Bool <$> bool
, Null <$ symbol "null"
]
-- Deriving with options
data Config = Config
{ configPort :: Int
, configHost :: Text
} deriving (Generic)
instance ToJSON Config where
toJSON = genericToJSON $ defaultOptions
{ fieldLabelModifier = drop 6 . camelTo2 '_' }
-- Manual instances for performance
instance FromJSON User where
parseJSON = withObject "User" $ \o -> do
userId <- o .: "id"
userName <- o .: "name"
userEmail <- o .:? "email"
pure User{..}
-- http-client with connection pooling
manager <- newManager defaultManagerSettings
{ managerConnCount = 100
, managerResponseTimeout = responseTimeoutMicro 30000000
}
-- req for type-safe requests
response <- runReq defaultHttpConfig $ do
req GET (https "api.example.com" /: "users")
NoReqBody jsonResponse
(header "Authorization" token)
-- Server with wai-websockets
wsApp :: ServerApp
wsApp pending = do
conn <- acceptRequest pending
withPingThread conn 30 (return ()) $ do
msg <- receiveData conn
sendTextData conn ("Echo: " <> msg)
-- Hashing
import Crypto.Hash
hash :: ByteString -> Digest SHA256
hash = hash
-- Symmetric encryption
import Crypto.Cipher.AES
encrypt :: ByteString -> ByteString -> ByteString -> ByteString
encrypt key iv = ecbEncrypt (initAES key)
-- Digital signatures
import Crypto.PubKey.Ed25519
sign :: SecretKey -> ByteString -> Signature
verify :: PublicKey -> ByteString -> Signature -> Bool
-- Make illegal states unrepresentable
data OrderStatus
= Draft (NonEmpty LineItem)
| Submitted SubmittedOrder
| Shipped ShippedOrder
| Delivered DeliveredOrder
| Cancelled CancelledOrder
-- Smart constructors with validation
newtype Email = Email Text
mkEmail :: Text -> Either ValidationError Email
mkEmail txt
| isValidEmail txt = Right (Email txt)
| otherwise = Left (InvalidEmail txt)
-- Type-safe state transitions
submitOrder :: Order 'Draft -> IO (Either OrderError (Order 'Submitted))
shipOrder :: Order 'Submitted -> ShippingInfo -> IO (Order 'Shipped)
-- Layer 1: Core business logic (pure)
calculateDiscount :: Customer -> Order -> Discount
-- Layer 2: Service layer (ReaderT)
type AppM = ReaderT AppEnv IO
getCustomerOrders :: CustomerId -> AppM [Order]
getCustomerOrders customerId = do
db <- asks appDatabase
liftIO $ queryOrders db customerId
-- Layer 3: HTTP/API layer
server :: ServerT API AppM
server = getCustomer :<|> createOrder :<|> listOrders
-- Core domain (pure)
module Domain.Order where
data Order = Order { ... }
-- Ports (interfaces)
class Monad m => OrderRepository m where
saveOrder :: Order -> m OrderId
findOrder :: OrderId -> m (Maybe Order)
-- Adapters (implementations)
instance OrderRepository (ReaderT PgConnection IO) where
saveOrder = pgSaveOrder
findOrder = pgFindOrder
-- Domain errors
data DomainError
= ValidationError ValidationError
| BusinessRuleViolation Text
| NotFound ResourceType ResourceId
deriving (Show, Eq)
-- Validation with Applicative
data UserForm = UserForm
{ formName :: Text
, formEmail :: Text
, formAge :: Int
}
validateUser :: UserForm -> Validation [ValidationError] User
validateUser form = User
<$> validateName (formName form)
<*> validateEmail (formEmail form)
<*> validateAge (formAge form)
-- Custom exceptions
data AppException
= DatabaseException Text
| NetworkException HttpException
| ParseException String
deriving (Show, Typeable)
instance Exception AppException
-- Safe resource management
withResource :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
withResource acquire release use = bracket acquire release use
-- Async exception safety
uninterruptibleMask_ $ do
criticalOperation
atomicWriteIORef state newState
-- Configuration types
data AppConfig = AppConfig
{ configDatabase :: DatabaseConfig
, configServer :: ServerConfig
, configLogging :: LogConfig
} deriving (Generic)
-- Loading with validation
loadConfig :: IO (Either ConfigError AppConfig)
loadConfig = do
env <- lookupEnv "APP_ENV"
let configFile = fromMaybe "config/development.yaml" env
yaml <- decodeFileEither configFile
traverse validateConfig yaml
-- Dhall for type-safe config
loadDhallConfig :: IO AppConfig
loadDhallConfig = input auto "./config.dhall"
-- Invariant testing
prop_sortIdempotent :: [Int] -> Bool
prop_sortIdempotent xs = sort (sort xs) == sort xs
-- Model-based testing
data Model = Model { modelItems :: Map ItemId Item }
data Command
= AddItem Item
| RemoveItem ItemId
| UpdateItem ItemId Item
runCommand :: Command -> State Model ()
prop_model :: [Command] -> Property
-- Test fixtures with finally
withTestDatabase :: (Connection -> IO a) -> IO a
withTestDatabase action = bracket
(setupTestDb >>= connect)
(\conn -> cleanupTestDb conn >> close conn)
action
-- Golden tests
goldenTest :: TestName -> FilePath -> IO ByteString -> TestTree
goldenTest name golden action = goldenVsString name golden (toLazy <$> action)
# 1. Examine project structure
find . -name "*.cabal" -o -name "stack.yaml" -o -name "package.yaml"
tree -I 'dist-newstyle|.stack-work' -L 2
# 2. Check build configuration
cabal configure --dry-run
grep -E "ghc-options|default-extensions" *.cabal
# 3. Identify key modules
find src -name "*.hs" | head -20
grep -h "^module" src/**/*.hs | sort | uniq
# 4. Review dependencies
cabal list-bins
cabal freeze --dry-run
-- Common type error patterns and solutions
-- 1. "Could not deduce" - Add type signature or constraint
function :: Num a => a -> a -- Add constraint
function x = x + 1
-- 2. "Ambiguous type" - Use TypeApplications
result = read @Int "42" -- Specify type explicitly
-- 3. "Rigid type variable" - Check scoping with ScopedTypeVariables
f :: forall a. a -> a
f x = let g :: a -> a -- 'a' same as outer
g = id
in g x
-- 4. "Infinite type" - Usually indicates missing base case
fix f = f (fix f) -- Needs type: fix :: (a -> a) -> a
-- Stack overflow diagnosis
-- Add strictness to accumulator
foldl' (!+) 0 xs -- Force evaluation
-- Pattern match failure
-- Use total functions
headMay :: [a] -> Maybe a
headMay [] = Nothing
headMay (x:_) = Just x
-- Lazy I/O issues
-- Use strict I/O or streaming
import qualified Data.ByteString as BS
content <- BS.readFile "large.txt" -- Strict read
Profile First:
cabal build --enable-profiling
cabal run myapp -- +RTS -p -hc -RTS
hp2ps -e8in -c myapp.hp
Identify Hotspots:
Memory Leak Detection:
# Heap profile by type
./myapp +RTS -hy -RTS
# Retainer profile
./myapp +RTS -hr -RTS
# Biographical profile
./myapp +RTS -hb -RTS
Space Leak Patterns:
-- Lazy accumulator (BAD)
sum [] acc = acc
sum (x:xs) acc = sum xs (acc + x)
-- Strict accumulator (GOOD)
sum [] !acc = acc
sum (x:xs) !acc = sum xs (acc + x)
-- Deadlock detection
-- Use STM with timeouts
atomicallyWithTimeout :: Int -> STM a -> IO (Maybe a)
atomicallyWithTimeout microseconds stm =
race (threadDelay microseconds) (atomically stm) >>= \case
Left _ -> return Nothing
Right a -> return (Just a)
-- Race condition prevention
-- Use STM for shared state
type Counter = TVar Int
incrementCounter :: Counter -> STM ()
incrementCounter counter = modifyTVar' counter (+1)
-- Thread debugging
-- Use labeled threads
myThread <- forkIO $ do
myThreadId >>= \tid -> labelThread tid "worker-thread"
workerLoop
-- 1. Start with types
data PaymentMethod
= CreditCard CardNumber CVV Expiry
| BankTransfer AccountNumber RoutingNumber
| PayPal Email
-- 2. Make illegal states unrepresentable
data Connection
= Disconnected
| Connecting ConnectionAttempt
| Connected Socket
| Failed Error
-- 3. Use phantom types for safety
newtype Id (a :: Type) = Id UUID
type UserId = Id User
type OrderId = Id Order
-- 4. Leverage type families
type family Result op where
Result 'Read = Maybe Document
Result 'Write = Either WriteError ()
Result 'Delete = Bool
-- | Process payment transaction.
--
-- Handles complete payment flow including:
--
-- * Validation payment details
-- * Communication with payment gateway
-- * Recording transaction in database
--
-- ==== Examples
--
-- >>> processPayment (CreditCard "4242424242424242" "123" "12/25") 99.99
-- Right (TransactionId "tx_abc123")
--
-- @since 1.0.0
processPayment
:: PaymentMethod
-- ^ Payment method to use
-> Amount
-- ^ Amount to charge
-> IO (Either PaymentError TransactionId)
-- ^ Returns either error or successful transaction ID
-- Property: Serialization roundtrip
prop_jsonRoundtrip :: User -> Property
prop_jsonRoundtrip user =
decode (encode user) === Just user
-- Property: Invariant preservation
prop_balanceNonNegative :: Account -> [Transaction] -> Property
prop_balanceNonNegative account txns =
let finalBalance = applyTransactions account txns
in finalBalance >= 0 ==> classify (finalBalance == 0) "zero balance" True
-- Unit test for edge case
test_emptyListHandling :: TestTree
test_emptyListHandling = testCase "handles empty list" $ do
result <- processItems []
result @?= EmptyResult
-- LEAK: Builds thunks
badSum :: [Int] -> Int
badSum = foldl (+) 0
-- FIX: Force evaluation
goodSum :: [Int] -> Int
goodSum = foldl' (+) 0
-- LEAK: Lazy record fields
data Stats = Stats
{ count :: Int
, total :: Double
}
-- FIX: Strict fields
data Stats = Stats
{ count :: !Int
, total :: !Double
}
-- LEAK: Retains entire list
average xs = sum xs / fromIntegral (length xs)
-- FIX: Single pass with strict accumulator
average xs = uncurry (/) $ foldl' (\(!s,!n) x -> (s+x,n+1)) (0,0) xs
-- PROBLEM: Overlapping instances
instance Show a => Show [a]
instance Show String -- Overlaps!
-- SOLUTION: Use newtype or OVERLAPPING pragma
newtype MyString = MyString String
instance Show MyString
-- PROBLEM: Non-injective type family
type family F a
type instance F Int = Bool
type instance F Char = Bool -- Same result!
-- SOLUTION: Use injective type family
type family G a = r | r -> a
-- SLOW: List operations
sumOfSquares :: [Int] -> Int
sumOfSquares = sum . map (^2)
-- FAST: Vector operations
import qualified Data.Vector.Unboxed as VU
sumOfSquares :: VU.Vector Int -> Int
sumOfSquares = VU.sum . VU.map (^2)
-- SLOW: String concatenation
concat :: [String] -> String
concat = foldr (++) ""
-- FAST: Text builder
import qualified Data.Text.Lazy.Builder as TB
concat :: [Text] -> Text
concat = TL.toStrict . TB.toLazyText . mconcat . map TB.fromText