Haskell Monad中`let .. in do`与`<-`的区别及字符串转Maybe Int列表报错解决
Great questions! Let's break them down one by one clearly.
1. Difference between let .. in do and <- in Haskell Monads
The core distinction lies in what they handle—pure values vs. monadic contexts:
The
<-operator: This is used to extract a pure value out of a monadic wrapper. For example, if you have a value of typem a(likeJust 5or[1,2,3]),<-lets you bind the inneravalue to a variable so you can work with it directly in thedoblock.Example with the
Maybemonad:addMaybe :: Maybe Int -> Maybe Int -> Maybe Int addMaybe mx my = do x <- mx -- Extracts Int from mx (if mx is Just x) y <- my -- Extracts Int from my (if my is Just y) return (x + y) -- Wraps the result back in MaybeHere,
xandyare pureIntvalues, notMaybe Int.letbindings indoblocks: These are for binding pure values that don't live inside a monad. In adoblock, you can omit theinkeyword for brevity—let x = ...just creates a pure variable that you can use anywhere in the rest of the block, no monadic context required.Example using the same
Maybemonad:addPure :: Maybe Int addPure = do let x = 3 -- Pure Int, no monadic wrapper let y = 4 -- Another pure Int return (x + y) -- Wraps the sum in Maybe, resulting in Just 7Here,
xandyare pure values from the start—we never extract them from a monadic type.
To summarize: Use <- when you need to unwrap a value from a monad (m a → a), and use let when you're working with pure expressions that don't need unwrapping.
2. Fixing the readInts function error
Let's first unpack why your code is throwing that type error:
Your initial code:
readInts (s::String) = do { ws <- words s; return (map (readMaybe::(String -> Maybe Int)) ws) }
The root cause
When you use ws <- words s in a do block, Haskell infers that you're working with the list monad ([]), since your intended return type is [Maybe Int]. In the list monad, <- iterates over each element of the list—so ws isn't the entire list of words (type [String]), but a single word (type String). Then you try to pass this single String to map, which expects a list as its second argument—hence the type mismatch (Expected type: [String], Actual type: String).
The fix
You have two clean ways to implement this correctly:
Option 1: Skip the do block (simplest)
Since you just need to map readMaybe over the list of words, you don't need a do block at all. Just compose the functions directly:
import Text.Read (readMaybe) readInts :: String -> [Maybe Int] readInts = map readMaybe . words
This takes the input string, splits it into words with words, then applies readMaybe to each word—exactly what your example expects.
Option 2: Use the do block correctly (if you prefer monadic syntax)
If you want to use a do block with the list monad, you need to process each word individually and return the Maybe Int for each one:
import Text.Read (readMaybe) readInts :: String -> [Maybe Int] readInts s = do word <- words s -- Iterate over each word in the list return (readMaybe word) -- Return the Maybe Int for each word
Here, the list monad automatically collects all the return results into a single [Maybe Int] list, which matches your desired output.
Either approach will produce the correct result: readInts "1 2 42 foo" = [Just 1,Just 2,Just 42,Nothing]
内容的提问来源于stack exchange,提问作者Tomasz Garbus

