如何用其他方式实现Haskell中MyData类型的ifA/ifB/ifC匹配函数?
Absolutely! There are several ways to implement those constructor-matching functions you wrote, and you’re right that the lens library has a very elegant solution for this kind of sum type manipulation. Let’s break this down step by step.
1. Simplified Vanilla Haskell (No Libraries)
Your original code is already clear and functional, but we can make it a bit more concise using lambda case syntax (requires the LambdaCase extension):
{-# LANGUAGE LambdaCase #-} data MyData = A Int | B String | C deriving (Show) matchA :: MyData -> Maybe Int matchA = \case A i -> Just i _ -> Nothing matchB :: MyData -> Maybe String matchB = \case B s -> Just s _ -> Nothing matchC :: MyData -> Maybe () matchC = \case C -> Just () _ -> Nothing
This does exactly the same work as your original ifA/ifB/ifC functions—it’s just a more compact way to write the pattern matching logic. Your existing example1 and example2 will work perfectly if you swap in these renamed functions.
2. Using the Lens Library (Prism for Sum Types)
You’re spot-on that lens has purpose-built tools for this scenario! The key component here is a Prism—an optic designed exclusively for working with sum types (like your MyData). Prisms let you:
- Extract a value from a specific constructor (returning
Maybeif the constructor doesn’t match) - Construct an instance of the sum type from a value for that constructor
Step 1: Generate Prisms Automatically
First, enable the TemplateHaskell extension and import the core lens modules. We can use makePrisms to auto-generate prisms for all constructors of MyData:
{-# LANGUAGE TemplateHaskell #-} import Control.Lens data MyData = A Int | B String | C deriving (Show) -- This generates prisms named _A, _B, _C automatically makePrisms ''MyData
Step 2: Extract Values with preview
The preview function takes a prism and a value, returning Just the inner value if the constructor matches, or Nothing otherwise. This is exactly what your original functions do:
-- Equivalent to ifA preview _A (A 42) -- Just 42 preview _A (B "hi") -- Nothing -- Equivalent to ifB preview _B (B "Hello") -- Just "Hello" preview _B C -- Nothing -- Equivalent to ifC preview _C C -- Just () preview _C (A 10) -- Nothing
Step 3: Rewrite Your Examples
Your example1 and example2 can be rewritten with these prisms, and even simplified using the >=> Kleisli composition operator (from Control.Monad):
import Control.Monad ( (>=>) ) mbMult3 :: Int -> Maybe Int mbMult3 i = Just (i * 3) concWorld :: String -> Maybe String concWorld str = Just (str ++ " World") -- Original monadic bind style example1 :: MyData -> Maybe Int example1 v = preview _A v >>= mbMult3 -- More concise with Kleisli composition example1' :: MyData -> Maybe Int example1' = preview _A >=> mbMult3 example2 :: MyData -> Maybe String example2 v = preview _B v >>= concWorld example2' :: MyData -> Maybe String example2' = preview _B >=> concWorld
Step 4: Manual Prism Definition (No TemplateHaskell)
If you prefer to avoid TemplateHaskell, you can define prisms manually using the prism function:
_A :: Prism' MyData Int _A = prism A $ \case A i -> Right i x -> Left x _B :: Prism' MyData String _B = prism B $ \case B s -> Right s x -> Left x _C :: Prism' MyData () _C = prism (const C) $ \case C -> Right () x -> Left x
The prism function takes two arguments:
- A constructor function (e.g.,
Aturns anIntintoMyData) - A matching function that returns
Rightthe inner value if it matches, orLeftthe original value otherwise.
Why Use Prisms?
Beyond just extracting values, prisms unlock more powerful workflows:
- Modify the inner value of a matching constructor with
overorset - Combine prisms with other optics (like lenses for product types)
- Use them with lens’s extensive combinators for complex data manipulation
内容的提问来源于stack exchange,提问作者Dulguun Otgon

