What is a clean way of writing this function? - haskell

I would like some help with a problem I'm trying to solve. Let's say I have a type called Thing:
data Thing = ....
And I want to write a function that, given a string, tries to match it with some stuff in my state and return a Thing:
findFirstMatch :: String -> State (Maybe Thing)
The thing is, to match that string, it needs a list of possible strings to match it with. That list is provided by a function defined for my state:
getPossibilities :: State String
Now, I need to call a third function that receives the original string and one of the possibilities, and returns a Maybe Thing:
tryToMatch :: String -> String -> State (Maybe Thing)
How can I write findFirstMatch? I thought of doing this but it doesn't seem that clean, and it feels like there might be something already implemented:
findFirstMatch :: String -> State (Maybe Thing)
findFirstMatch str = do
xs <- getPossibilities
firstNotNull (map (tryToMatch str) xs)
firstNotNull :: [State (Maybe Thing)] -> State (Maybe Thing)
firstNotNull [] = return Nothing
firstNotNull (x:xs) = do
r <- x
case r of
Just _ -> return r
Nothing -> firstNotNull xs

First off, you can clean this up quite a bit if you write firstNotNull without using State. A very simple pass would be:
firstNotNull :: [Maybe Thing] -> Maybe Thing
firstNotNull [] = Nothing
firstNotNull (Just x:_) = Just x
firstNotNull Nothing:xs = firstNotNull xs
Furthermore, you can simplify even further by using some functions from Data.Maybe:
import Data.Maybe (catMaybes, listToMaybe)
firstNotNull :: [Maybe a] -> Maybe a
firstNotNull = listToMaybe . catMaybes
Now, let's turn our attention to findFirstMatch to see how we can use this simplified version of firstNotNull. The first question is: Does tryToMatch really need to live in State? After all, it already has access to both Strings that it's matching on. If you can change its type to tryToMatch :: String -> String -> Maybe Thing, then you're basically good to go.
On the other hand, if tryToMatch really does need to live in State, then there's just a little bit more to do: we need to pass firstNotNull a [Maybe Thing], but we have a [State (Maybe Thing)]. We can fix this by using sequenceA, as in:
findFirstMatch :: String -> State (Maybe Thing)
findFirstMatch str = do
xs <- getPossibilities
fmap firstNotNull $ sequenceA (map (tryToMatch str) xs)
Note that this only works if your State monad is lazy enough. If it's too strict, it will end up finding all matches, doing far too much work (and screwing up performance) and then return the first one.
From here, we can recognize that the usage of sequenceA and map can be reduced to a single call to traverse, as in:
fmap firstNotNull $ traverse (tryToMatch str) xs
This seems much cleaner!
Of course, we can still go further if we really want. It's not clear that the below changes actually make the code cleaner (rather, there's a strong argument that they make it harder to read), but let's have some fun anyway.
Rather than use do, we can choose to make this a one-liner with an appropriate use of monadic bind:
findFirstMatch str = getPossibilities $ \xs -> (fmap firstNotNull $ sequenceA (map (tryToMatch str) xs))
The inner lambda can be nicely eta-reduced to:
findFirstMatch str = getPossibilities >>= fmap firstNotNull . sequenceA . map (tryToMatch str)
And this too can be eta-reduced:
findFirstMatch = (getPossibilities >>=) . ((fmap firstNotNull . sequenceA) .) . map . tryToMatch
And while we're at it, why even have a definition of firstNotNull when we can inline it!
findFirstMatch :: String -> State (Maybe Thing)
findFirstMatch = (getPossibilities >>=) . ((fmap (listToMaybe . catMaybes) . sequenceA) .) . map . tryToMatch
There, your whole function in one messy line!

Related

How to convert a haskell List into a monadic function that uses list values for operations?

I am having trouble wrapping my head around making to work a conversion of a list into a monadic function that uses values of the list.
For example, I have a list [("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")] that I want to be converted into a monadic function that is mapped to a following do statement:
do
mkBlob ("dir1/content1", "1")
mkBlob ("dir1/content11", "11")
mkBlob ("dir2/content2", "2")
mkBlob ("dir2/content21", "21")
I imagine it to be a function similar to this:
contentToTree [] = return
contentToTree (x:xs) = (mkBlob x) =<< (contentToTree xs)
But this does not work, failing with an error:
• Couldn't match expected type ‘() -> TreeT LgRepo m ()’
with actual type ‘TreeT LgRepo m ()’
• Possible cause: ‘(>>=)’ is applied to too many arguments
In the expression: (mkBlob x) >>= (contentToTree xs)
In an equation for ‘contentToTree’:
contentToTree (x : xs) = (mkBlob x) >>= (contentToTree xs)
• Relevant bindings include
contentToTree :: [(TreeFilePath, String)] -> () -> TreeT LgRepo m ()
I do not quite understand how to make it work.
Here is my relevant code:
import Data.Either
import Git
import Data.Map
import Conduit
import qualified Data.List as L
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as BL
import Control.Monad (join)
type FileName = String
data Content = Content {
content :: Either (Map FileName Content) String
} deriving (Eq, Show)
contentToPaths :: String -> Content -> [(TreeFilePath, String)]
contentToPaths path (Content content) = case content of
Left m -> join $ L.map (\(k, v) -> (contentToPaths (if L.null path then k else path ++ "/" ++ k) v)) $ Data.Map.toList m
Right c -> [(BS.pack path, c)]
mkBlob :: MonadGit r m => (TreeFilePath, String) -> TreeT r m ()
mkBlob (path, content) = putBlob path
=<< lift (createBlob $ BlobStream $
sourceLazy $ BL.fromChunks [BS.pack content])
sampleContent = Content $ Left $ fromList [
("dir1", Content $ Left $ fromList [
("content1", Content $ Right "1"),
("content11", Content $ Right "11")
]),
("dir2", Content $ Left $ fromList [
("content2", Content $ Right "2"),
("content21", Content $ Right "21")
])
]
Would be grateful for any tips or help.
You have:
A list of values of some type a (in this case a ~ (String, String)). So, xs :: [a]
A function f from a to some type b in a monadic context, m b. Since you're ignoring the return value, we can imagine b ~ (). So, f :: Monad m => a -> m ().
You want to perform the operation, yielding some monadic context and an unimportant value, m (). So overall, we want some function doStuffWithList :: Monad m => [a] -> (a -> m ()) -> m (). We can search Hoogle for this type, and it yields some results. Unfortunately, as we've chosen to order the arguments, the first several results are little-used functions from other packages. If you scroll further, you start to find stuff in base - very promising. As it turns out, the function you are looking for is traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f (). With that, we can replace your do-block with just:
traverse_ mkBlob [ ("dir1/content1", "1")
, ("dir1/content11", "11")
, ("dir2/content2", "2")
, ("dir2/content21", "21")
]
As it happens there are many names for this function, some for historical reasons and some for stylistic reasons. mapM_, forM_, and for_ are all the same and all in base, so you could use any of these. But the M_ versions are out of favor these days because really you only need Applicative, not Monad; and the for versions take their arguments in an order that's convenient for lambdas but inconvenient for named functions. So, traverse_ is the one I'd suggest.
Assuming mkBlob is a function that looks like
mkBlob :: (String, String) -> M ()
where M is some specific monad, then you have the list
xs = [("dir1/content1", "1"), ("dir1/content11", "11"), ("dir2/content2", "2"), ("dir2/content21", "21")]
whose type is xs :: [(String, String)]. The first thing we need is to run the mkBlob function on each element, i.e. via map.
map mkBlob xs :: [M ()]
Now, we have a list of monadic actions, so we can use sequence to run them in sequence.
sequence (map mkBlob xs) :: M [()]
The resulting [()] value is all but useless, so we can use void to get rid of it
void . sequence . map mkBlob $ xs :: M ()
Now, void . sequence is called sequence_ in Haskell (since this pattern is fairly common), and sequence . map is called mapM. Putting the two together, the function you want is called mapM_.
mapM_ mkBlob xs :: M ()

Haskell: Generate an array out of multiple maybes

I have simple question but I can't find an elegant solution.
I have a function like this:
getFoos :: Maybe Int
-> Maybe Int
-> Maybe Int
-> [(String, Int)]
getFoos ma mb mc = ...
I would like to create this array: [("A": 1), ("B": 2), ("C": 3)].
But that's only if the three arguments are Just! If one or several are Nothing, they will simply not appear in the resulting array.
Any elegant way to accomplish this?
For info, the real signature of my function is:
getQuerySelect :: Maybe Token
-> Maybe DeviceId
-> Maybe SensorId
-> Maybe Int
-> Maybe Int
-> Maybe Int
-> Maybe UTCTime
-> Maybe UTCTime
-> [(String, String)]
It's meant for database query with MongoDB.
My design so far:
getQuerySelect mdev msen ... = do
let selDev = [("device_id", dev) | (Just (DeviceId dev)) <- [mdev]]
let selSen = [("sensor_id", sen) | (Just (SensorId sen)) <- [msen]]
...
return $ selDev <> selSen <> ...
Works, but still a bit awkward in my opinion.
If you want it to "scale well for more Maybes", then just work with an array in the first place:
getFoosArr :: [Maybe Int] -> [(String, Int)]
The implementation could then be e.g.:
getFoosArr = catMaybes . zipWith (\a -> fmap (a,)) [A..]
First we tag each Maybe Int with a letter1, and then it's just a matter of filtering out the Justs with catMaybes.
And of course your original function would simply need to pass the arguments then:
getFoos ma mb mc = getFoosArr [ma, mb, mc]
If you have heterogenous types, but want to convert it all to String anyway, it's easy; make getFoosArr take [Maybe String], and convert the Justs before passing further:
getFoosHet ma mb mc = getFoosArr
[ fmap showA ma
, fmap showB mb
, fmap showC mc
]
I'm assuming showA :: A -> String, ma :: Just A.
1 I've eta-reduced \a b -> fmap (a,) b to \a -> fmap (a,). It could also be written as fmap . (,), but I personally find it a bit less readable.

Is there a pre-defined function for conduit analogy of `takeWhile`?

I find the following function missing from the Data.Conduit.List module, and I couldn't find an easy way to compose this using functions in that module.
takeWhile :: Monad m => (a -> Bool) -> Consumer a m [a]
takeWhile p = await >>= \case
Nothing -> return []
Just b -> if p b
then (b :) <$> takeWhile p
else (leftover b) >> return []
This function is very useful in my application where I sometimes need to group the next few items together, and I am not sure how many are there.
The missing of this function is kind of strange to me as there are take :: Monad m => Int -> Consumer a m [a], and groupBy :: Monad m => (a -> a -> Bool) -> Conduit a m [a], but no takeWhile.
Am I missing something?
Edit: Per #ErikR's request, here is two simple examples that can perhaps clarify why I think this function could be useful.
Case 1: the protocol specifies there be a header section in the stream. For simplicity let's assume it's a String stream and the header items are marked by a leading #.
Stream content:
#language=English
#encoding=Unicode
Apple
Orange
Blue
Red
Sheep
Dog
...
Code using takeWhile:
myConduit :: Conduit String IO String ()
myConduit = do
headers <- takeWhile ((== '#') . head)
awaitForever $ \ item -> do
case getLanguage headers of
English -> ...
French -> ...
Case 2: the protocol specifies that items with prefix # has several continuations prefixed by +.
Stream content:
Apple
Orange
Blue
#Has
+kell
#A
+Really
+Long
+Word
Dog
...
Code using takeWhile:
myConduit :: Conduit String IO String ()
myConduit = runMaybeC . forever $ do
a <- maybe (lift mzero) return =<< await
aConts <- if head item == '#' then takeWhile ((== '+') . head)
else return []
liftIO . putStrLn . concat $ a : aConts
However, aside from being useful, it is also for completeness. I see that Data.Conduit.List's goal is to provide a set of "list-like" operations in the Conduit context. I think bread-and-butter functions like takeWhile should be provided, along with its siblings like dropWhile, so that people don't have to change their style of coding when thinking about conduits as lists.

How do I "continue" in a `Monad` loop?

Often times I found myself in need of skipping the rest of the iteration (like continue in C) in Haskell:
forM_ [1..100] $ \ i ->
a <- doSomeIO
when (not $ isValid1 a) <skip_rest_of_the_iteration>
b <- doSomeOtherIO a
when (not $ isValid2 b) <skip_rest_of_the_iteration>
...
However, I failed to find an easy way to do so. The only way I am aware of is probably the Trans.Maybe, but is it necessary to use a monad transform to achieve something so trivial?
Remember that loops like this in Haskell are not magic...they're just normal first-class things that you can write yourself.
For what it's worth, I don't think it's too useful to think of MaybeT as a Monad transformer. To me, MaybeT is just a newtype wrapper to give an alternative implementation of (>>=)...just like how you use Product, Sum, First, And, etc. to give alternative implementations of mappend and mempty.
Right now, (>>=) for you is IO a -> (a -> IO b) -> IO b. But it'd be more useful to have (>>=) here be IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b). As soon as you get to the first action that returns a Nothing, it's really impossible to "bind" any further. That's exactly what MaybeT gives you. You also get a "custom instance" of guard, guard :: Bool -> IO (Maybe a), instead of guard :: IO a.
forM_ [1..100] $ \i -> runMaybeT $ do
a <- lift doSomeIO
guard (isValid1 a)
b <- lift $ doSomeOtherIO a
guard (isValid2 b)
...
and that's it :)
MaybeT is not magic either, and you can achieve basically the same effect by using nested whens. It's not necessary, it just makes things a lot simpler and cleaner :)
Here's how you would do it using bare-bones recursion:
loop [] = return () -- done with the loop
loop (x:xs) =
do a <- doSomeIO
if ...a...
then return () -- exit the loop
else do -- continuing with the loop
b <- doSomeMoreIO
if ...b...
then return () -- exit the loop
else do -- continuing with the loop
...
loop xs -- perform the next iteration
and then invoke it with:
loop [1..100]
You can tidy this up a bit with the when function from Control.Monad:
loop [] = return ()
loop (x:xs) =
do a <- doSomeIO
when (not ...a...) $ do
b <- doSomeMoreIO
when (not ...b...) $ do
...
loop xs
There is also unless in Control.Monad which you might prefer to use.
Using #Ørjan Johansen 's helpful advice, here is an simple example:
import Control.Monad
loop [] = return ()
loop (x:xs) = do
putStrLn $ "x = " ++ show x
a <- getLine
when (a /= "stop") $ do
b <- getLine
when (b /= "stop") $ do
print $ "iteration: " ++ show x ++ ": a = " ++ a ++ " b = " ++ b
loop xs
main = loop [1..3]
If you want to loop over a list or other container to perform actions and/or produce a summary value, and you're finding the usual convenience tools like for_ and foldM aren't good enough for the job, you might want to consider foldr, which is plenty strong enough for the job. When you're not really looping over a container, you can use plain old recursion or pull in something like https://hackage.haskell.org/package/loops or (for a very different flavor) https://hackage.haskell.org/package/machines or perhaps https://hackage.haskell.org/package/pipes.

How do you fmap a Getter?

As discussed on reddit, you can't just lift a Lens' a b to Lens' (Maybe a) (Maybe b). But for the special case Getter a b, this is obviously possible, since it's isomorphic to a->b. But unlike with Iso, there appears to be no standard function to perform this lift.
What's the preferred way to do that? In cases like
someFunction $ myMap^.at(i).ꜰᴍᴀᴘGᴇᴛ(mySubGetter)
I could of course do
someFunction $ myMap^.at(i) & fmap (^.mySubGetter)
but that doesn't work as well in other applications, as when operating on a state monad.
foo <- use $ myMapInState.at(i).ꜰᴍᴀᴘGᴇᴛ(mySubGetter)
I believe you can accomplish what you want with a prism.
If your values have these types:
myMap :: Map String (Int, String)
myMap = mempty
mySubGetter :: Lens' (Int, String) String
mySubGetter = _2
then you can do:
myVal :: Maybe String
myVal = myMap ^? at "myKey" . _Just . mySubGetter
If you just want to apply a function to a getter you can use the to function from Control.Lens.Getter, you have to manually deal with the sublens though:
someFunction $ myMap ^. at(i) . to (fmap (^. mySubGetter))

Resources