I'm trying to build a string from optional arguments. For example to generate a greeting string from a title and a name This is trivial in a imperative language and would look like this
def greeting(title, name):
s = "Hello"
if a :
s += "Mr"
if b:
s += b
My first attempt in haskell is :
greeting :: Bool-> Maybe String -> String
greeting title name = foldl (++) "Hello" (catMaybes [title' title, name])
where
title' True = Just "Mr"
title' False = Nothing
I'm sure there is a bette way to do it. First, I'm sure this foldl catMaybes combination exists somewhere but I couldn't find it. Second, folding works here because I'm using the same operation (and the same type). So what is there a better way to do it ?
I was also thinking using a Writer but I'm not sure either how to do it.
Update
This is only an example (maybe bad or too simple). My question is more , how to generalize it to many arguments .
So the real problem is not just about concatening 2 strings but more how to generate letters from a template with optional sections and parameters, like you would do in Word with the mail merge tool.
You have on one a csv file with customer name, telephone number, how much is overdue etc. Some of the field could be optional. On the other side you have a template and the goal is to generate one letter per customer (row) according to the *template. The question is then how do you write this template in haskell (without the help of any templating library).
In my first example the template will be "hello (Mr){name}" but in the real word the template could be in invoice, a statement letter or even a complete accounting report.
Actually Haskell is smarter than any imperative approach.
Let's imagine name has a value Nothing. Does it make sense to render something like "Hello Mr"? Probably it would make more sense to consider it as an exceptional situation, and for that we can again use Maybe. So first of all, I'd update the signature of the function to the following:
greeting :: Bool -> Maybe String -> Maybe String
Now we can look at our problem again and find out that Haskell provides multiple ways to approach it.
Hello, Monads
Maybe is a monad, so we can use the do syntax with it:
greeting mr name = do
nameValue <- name
return $ if mr
then "Hello, Mr. " ++ nameValue
else "Hello, " ++ nameValue
Hello, Functors
Maybe is also a functor, so alternatively we can use fmap:
greeting mr name =
if mr
then fmap ("Hello, Mr. " ++) name
else fmap ("Hello, " ++) name
We can also do a bit of refactoring, if we consider the signature as the following:
greeting :: Bool -> (Maybe String -> Maybe String)
i.e., as a function of one argument, which returns another function. So the implementaion:
greeting mr =
if mr
then fmap ("Hello, Mr. " ++)
else fmap ("Hello, " ++)
or
greeting True = fmap ("Hello, Mr. " ++)
greeting False = fmap ("Hello " ++)
if you find this syntax better.
Haskell is so good at abstractions, it can easily replicate the imperative patterns. What you're doing in your example is called a "builder" pattern. Writer is a monad, which wraps a pattern of accumulation or "building" of any Monoid data, and String is a Monoid.
import Control.Monad.Writer hiding (forM_)
import Data.Foldable
greeting :: Bool -> Maybe String -> String -> String
greeting mr name surname =
execWriter $ do
tell $ "Hello,"
when mr $ tell $ " Mr."
forM_ name $ \s -> tell $ " " ++ s
tell $ " " ++ surname
tell $ "!"
main = do
putStrLn $ greeting False (Just "Ray") "Charles"
putStrLn $ greeting True Nothing "Bean"
Outputs:
Hello, Ray Charles!
Hello, Mr. Bean!
You could avoid using a Maybe for the title and do:
greeting :: Bool-> Maybe String -> String
greeting title name = "Hello" ++ title' ++ (maybe "" id name)
where title' = if title then "Mr" else ""
If you have a number of Maybes you could use mconcat since String is a monoid:
import Data.Monoid
import Data.Maybe
greeting :: [Maybe String] -> String
greeting l = fromJust $ mconcat ((Just "Hello"):l)
I think that your function is violating the SRP (Single responsibility principle). It is doing two things:
prefixing the name with Mr
rendering the greeting message
You can notice that the Bool and String (name) refer to the same "entity" (a person).
So let's define a person:
data Person
= Mister String
| Person String
deriving (Eq)
We can now create an instance of show that makes sense:
instance Show Person where
show (Mister name) = "Mr. " ++ name
show (Person name) = name
and finally we can reformulate your greeting function as:
greeting :: Maybe Person -> String
greeting (Just person) = "Hello, " ++ show person
greeting Nothing = "Hello"
Live demo
The code is simple, readable and just few lines longer (don't be afraid of writing code). The two actions (prefixing and greeting) are separated and everything is much simpler.
If you really have to, you can trivially create a function to generate a Mister or a Person based on a boolean value:
makePerson :: Bool -> String -> Person
makePerson True = Mister
makePerson False = Person
Live demo
I know it's an old post ... but it may be useful ...
in a pure "functional" way ...
greeting :: Maybe String -> String -> String
greeting Nothing name = "Hello" ++ ", " ++ name
greeting (Just title) name = "Hello" ++ ", " ++ title ++ " " ++ name ++ "!"
how to call :
-- greeting Nothing "John"
-- greeting Nothing "Charlotte"
-- greeting (Just "Mr.") "John"
-- greeting (Just "Miss.") "Charlotte"
Related
I have been trying to get to grips with the reader monad and came across this tutorial. In it, the author presents this example:
example2 :: String -> String
example2 context = runReader (greet "James" >>= end) context
where
greet :: String -> Reader String String
greet name = do
greeting <- ask
return $ greeting ++ ", " ++ name
end :: String -> Reader String String
end input = do
isHello <- asks (== "Hello")
return $ input ++ if isHello then "!" else "."
I know that this is a trivial example that shows the mechanics, but I am trying to figure out why it would be better than doing something like:
example3 :: String -> String
example3 = end <*> (greet "James")
where
greet name input = input ++ ", " ++ name
end input = if input == "Hello" then (++ "!") else (++ ".")
Reader isn't often used by itself in real code. As you have observed, it's not really better than just passing an extra argument to your functions. However, as part of a monad transformer it is an excellent way to pass configuration parameters through your application. Usually this is done by adding a MonadReader constraint to any function that needs access to configuration.
Here's an attempt at a more real-world example:
data Config = Config
{ databaseConnection :: Connection
, ... other configuration stuff
}
getUser :: (MonadReader Config m, MonadIO m) => UserKey -> m User
getUser x = do
db <- asks databaseConnection
.... fetch user from database using the connection
then your main would look something like:
main :: IO ()
main = do
config <- .... create the configuration
user <- runReaderT (getUser (UserKey 42)) config
print user
dfeuer, chi and user2297560 are right in that "Reader isn't often used by itself in real code". It is worth noting, though, that there is next to no essential difference between what you do in the second snippet in the question and actually using Reader as a monad: the function functor is just Reader without the wrappers, and the Monad and Applicative instances for both of them are equivalent. By the way, outside of highly polymorphic code1, the typical motivation for using the function Applicative is making code more pointfree. In that case, moderation is highly advisable. For instance, as far as my own taste goes, this...
(&&) <$> isFoo <*> isBar
... is fine (and sometimes it might even read nicer than the pointful spelling), while this...
end <*> greet "James"
... is just confusing.
Footnotes
For instance, as Carl points out in a comment, it and the related instances can be useful in...
[...] places where you have code that's polymorphic in a type constructor and your use case is passing an argument in. This can come up when using the polymorphic types offered by lenses, for instance.
I'm trying to make sense of one of the examples presented at the pipes tutorial concerning ListT:
import Pipes
import qualified Pipes.Prelude as P
input :: Producer String IO ()
input = P.stdinLn >-> P.takeWhile (/= "quit")
name :: ListT IO String
name = do
firstName <- Select input
lastName <- Select input
return (firstName ++ " " ++ lastName)
If the example above is run, we get output like the following:
>>> runEffect $ every name >-> P.stdoutLn
Daniel<Enter>
Fischer<Enter>
Daniel Fischer
Wagner<Enter>
Daniel Wagner
quit<Enter>
Donald<Enter>
Stewart<Enter>
Donald Stewart
Duck<Enter>
Donald Duck
quit<Enter>
quit<Enter>
>>>
It seems that:
When you run this (on ghci), the first name you input will get bound and only the second will change. I would expect that both producers (defined by Select input) will take turns (maybe non-deterministically) at reading the input.
Entering quit one time will allow to re-bind the first name. Again, I fail to see why firstName will get bound to the first value entered by the user.
Entering quit twice in a row will terminate the program. However, I would expect that quit only has to be entered twice to quit the program (possibly alternating with other input).
I'm missing something fundamental about the way the example above works, but I cannot see what.
When you run this (on GHCi), the first name you input will get bound
and only the second will change. I would expect that both producers
(defined by Select input) will take turns (maybe
non-deterministically) at reading the input.
ListT doesn't work that way. Instead, it is "depth-first". Every time it gets a first name, it starts reading the whole list of last names anew.
The example doesn't do that, but each list of last names could depend on the first name that has been read previously. Like this:
input' :: String -> Producer String IO ()
input' msg =
(forever $ do
liftIO $ putStr msg
r <- liftIO $ getLine
yield r
) >-> P.takeWhile (/= "quit")
name' :: ListT IO String
name' = do
firstName <- Select input
lastName <- Select $ input' $ "Enter a last name for " ++ firstName ++ ": "
return (firstName ++ " " ++ lastName)
Entering quit one time will allow to re-bind the first name. Again, I
fail to see why firstName will get bound to the first value entered by
the user.
If we are reading last names and encounter a quit command, that "branch" terminates and we go back to the level above, to read another first name from the list. The "effectful list" that reads the last names is only re-created after we have a first name to work with.
Entering quit twice in a row will terminate the program. However, I
would expect that quit only has to be entered twice to quit the
program (possibly alternating with other input).
Notice that entering a single quit at the very beginning will also terminate the program, as we are "closing" the top-level list of first names.
Basically, every time you enter a quit you close the current branch and go up a level in the "search tree". Every time you enter a first name you go down one level.
To add to #danidiaz's great answer, you can get the behavior of name prompting for a firstName again, instead of just keep asking for lastNames.
What you can do is make use of the MonadZip instance of Monad m => ListT m, particularly
mzip :: Monad m => ListT m a -> ListT m b -> ListT m (a, b).
Thus, you could define name as
name :: ListT IO String
name = do
(firstName, lastName) <- mzip (Select input) (Select input)
return (firstName ++ " " ++ lastName)
and you get your alternating behavior.
As a nice aside, you can use the MonadComprehensions and ParallelListComp extensions as well. Using these, the two versions of name become
name :: ListT IO String
name = [ firstName ++ " " ++ lastName | firstName <- Select input
, lastName <- Select input ]
name' :: ListT IO String
name' = [ firstName ++ " " ++ lastName | firstName <- Select input
| lastName <- Select input ]
So I'm trying to do something that's kind of novel (I think), but I'm not
experienced enough with Haskell type-level programming to work it out myself.
I've got a free monad describing some effects to perform (an AST, if that's the
way you roll), and I want to interpret it against some description of the
effects that are expected.
Here's my code so far::
{-# LANGUAGE DeriveFunctor, FlexibleInstances, GADTs, FlexibleContexts #-}
import Control.Monad.Free -- from package 'free'
data DSL next
= Prompt String (String -> next)
| Display String next
deriving (Show, Functor)
prompt p = liftF (Prompt p id)
display o = liftF (Display o ())
-- |Just to make sure my stuff works interactively
runIO :: (Free DSL a) -> IO a
runIO (Free (Prompt p cont)) = do
putStr p
line <- getLine
runIO (cont line)
runIO (Free (Display o cont)) = do putStrLn o; runIO cont
runIO (Pure x) = return x
That's the "core" code. Here's an example program:
greet :: (Free DSL ())
greet = do
name <- prompt "Enter your name: "
let greeting = "Why hello there, " ++ name ++ "."
display greeting
friendName <- prompt "And what is your friend's name? "
display ("It's good to meet you too, " ++ friendName ++ ".")
To test this program, I want to use a function runTest :: Free DSL a -> _ -> Maybe a, which should take a program and some specification of "expected effects" vaguely like this:
expect = (
(Prompt' "Enter your name:", "radix"),
(Display' "Why hello there, radix.", ()),
(Prompt' "And what is your friend's name?", "Bob"),
(Display' "It's good to meet you too, Bob.", ()))
and interpret the program by matching each effect that it performs against the next item in the expect list. Then the associated value (the second item in each pair) should be returned as the result of that effect to the program. If all the effects match, the final result of the program should be returned as a Just. If something doesn't match, Nothing should be returned (later I'll expand this so that it returns an informative error message).
Of course this expect tuple is useless, since its type is a big giant thing that I can't write a generic runTest function over. The main problem I'm having is how I should represent this sequence of expected intents in a way that I can write a function that works with any sequence against any program Free DSL a.
I'm vaguely aware of various advanced type-level features in Haskell, but I'm not yet experienced to know which things I should try to use.
Should I be using an HList or something for my expected sequence?
Any hints for things to look into are greatly appreciated.
A test for a program Free f a is just an interpreter for the program Free f a -> r producing some result r
What you are looking for is an easy way to build interpreters for the program that assert that the outcome of the program is what you expected. Each step of the interpreter will either unwrap a Free f instruction from the program or describe some error. They'll have the type
Free DSL a -> Either String (Free DSL a)
| | ^ the remaining program after this step
| ^ a descriptive error
^ the remaining program before this step
We'll make a test for each of the constructors in the DSL. prompt' expects a Prompt with a specific value and provides the response value to the function to find what's next.
prompt' :: String -> String -> Free DSL a -> Either String (Free DSL a)
prompt' expected response f =
case f of
Free (Prompt p cont) | p == expected -> return (cont response)
otherwise -> Left $ "Expected (Prompt " ++ show expected ++ " ...) but got " ++ abbreviate f
abbreviate :: Free DSL a -> String
abbreviate (Free (Prompt p _)) = "(Free (Prompt " ++ show p ++ " ...))"
abbreviate (Free (Display p _)) = "(Free (Display " ++ show p ++ " ...))"
abbreviate (Pure _) = "(Pure ...)"
display' expects a Display with a specific value.
display' :: String -> Free DSL a -> Either String (Free DSL a)
display' expected f =
case f of
Free (Display p next) | p == expected -> return next
otherwise -> Left $ "Expected (Display " ++ show expected ++ " ...) but got " ++ abbreviate f
pure' expects a Pure with a specific value
pure' :: (Eq a, Show a) => a -> Free DSL a -> Either String ()
pure' expected f =
case f of
Pure a | a == expected -> return ()
otherwise -> Left $ "Expected " ++ abbreviate' (Pure expected) ++ " but got " ++ abbreviate' f
abbreviate' :: Show a => Free DSL a -> String
abbreviate' (Pure a) = "(Pure " ++ showsPrec 10 a ")"
abbreviate' f = abbreviate f
With prompt' and display' we can easily build an interpreter in the style of expect.
expect :: Free DSL a -> Either String (Free DSL a)
expect f = return f >>=
prompt' "Enter your name:" "radix" >>=
display' "Why hello there, radix." >>=
prompt' "And what is your friend's name?" "Bob" >>=
display' "It's good to meet you too, Bob."
Running this test
main = either putStrLn (putStrLn . const "Passed") $ expect greet
Results in a failure
Expected (Prompt "Enter your name:" ...) but got (Free (Prompt "Enter your name: " ...))
Once we change the test to expect spaces at the end of the prompts
expect :: Free DSL a -> Either String (Free DSL a)
expect f = return f >>=
prompt' "Enter your name: " "radix" >>=
display' "Why hello there, radix." >>=
prompt' "And what is your friend's name? " "Bob" >>=
display' "It's good to meet you too, Bob."
Running it results in
Passed
I want the user to enter a list of tuples to search on it for a key like this can I say it like this
data BookInfo = Book Int String [String]
deriving(Show)
findbook :: [BookInfo] -> Int -> BookInfo
findbook vs key = (booker (vs!!(bookFinding vs key 0 (length vs))) key)
getBookInfo = do
putStrLn "Enter book id :"
k <- read
putStrLn "Enter book name : "
r <- getLine
putStrLn "Enter book subject :"
m <- getLine
let Book book = enter k r [m]
return book
main = do
putStr "Enter you first info is :"
v <- getBookInfo
let Book vs = v:[]
c <- getLine
if c == "N"
then
putStr "You done"
else
Book booke = getBookInfo
vs = booke:vs
putStr "Do you want to search ? :"
m <- getch
if m == 'y'
then
putStr " Enter your key :"
s <- readNum
let Book w = findBook vs s
putStrLn" The result is: " ++ show(w)
But it gives me an error:
The last statement in do must be m <- getch
What am I doing wrong?
It looks like you're trying to write a program to read a list of books into some sort of database, and then search the books. You start off well, with a data type that stores book info:
data BookInfo = Book Int String [String] deriving (Show)
Now let's look at your function which reads in book info from the user.
getBookInfo = do
putStrLn "Enter book id :"
k <- read
putStrLn "Enter book name : "
r <- getLine
putStrLn "Enter book subject :"
m <- getLine
let Book book = enter k r [m]
return book
I fixed up your indentation for you (here's a tip: use four spaces for indentation!) but there are other problems:
The line k <- read doesn't make any sense. The type of read is read :: Read a => String -> a but you're using it as if it was an I/O action. You need the function readLn instead.
The line let Book book = enter k r [m] doesn't make any sense. It looks like you're used to writing in a language like C or Java, where you have to specify types. You don't have to do that in Haskell! Also, the enter function is unnecessary. You can just write let book = Book k r [m] and that will work fine. In fact, you don't need the temporary variable book at all - you can create the Book and return it all in one line.
So you can write:
getBookInfo :: IO BookInfo
getBookInfo = do
putStrLn "Enter book id: "
bookid <- readLn
putStrLn "Enter book name: "
name <- getLine
putStrLn "Enter book subject: "
subject <- getLine
return (Book bookid name [subject])
which will now compile fine. Notice that I also added a type declaration (which is optional).
Your next function, main, is trying to do a bit too much. It contains all the rules for getting a list of books, and the rules for searching them. These are two separate tasks, so they should be in two separate functions. So let's write a function that gets a list of books:
getBookList :: IO [BookInfo]
getBookList = do
putStr "Any more books? "
answer <- getLine
if answer == "N"
then return []
else do
book <- getBookInfo
books <- getBookList
return (book:books)
Take a while to understand how this function works. First it asks if you have any more books to enter. If you say "N" then it returns the empty list, and you're done. Otherwise, it does something magical - first, it calls getBookInfo to get the info for a single book. Then it calls itself to get a list of books! This is an example of recursion. Finally, it adds the first book to the list of books, and returns the whole list.
You should have a go at writing the rest of the program yourself now. I may revisit this answer in a day or so to add some more detail. I'm more likely to do that if you leave a comment letting me know what you're tried, and where you get stuck. Remember:
Indent your code properly! Use four spaces. Get an editor like Sublime Text 2 that knows how to handle indentation.
Try and write small (less then 10 lines) functions and chain them together to make a complete program. This will prevent you from losing your mind when trying to debug the 400 line monstrosity you've come up with.
Check the types! You can load up ghci and type, for example, :t read to see the type of the read function.
How do I get a search match from a list of strings in Haskell?
module Main
where
import List
import IO
import Monad
getLines = liftM lines . readFile
main = do
putStrLn "Please enter your name: "
name <- getLine
list <- getLines "list.txt"
-- mapM_ putStrLn list -- this part is to list out the input of lists
The first thing to do, the all-important first principle, is to get as much of the thinking out of main or out of IO as possible. main should where possible contain all the IO and maybe nothing but IO decorated with pure terms you define elsewhere in the module. Your getLines is mixing them unnecessarily.
So, to get that out of the way, we should have a main that is something like
main =
do putStrLn "What is your name?"
name <- getContents
names <- readFile "names.txt"
putStrLn (frankJ name names)
-- or maybe the more austere segregation of IO from all else that we get from:
main =
do putStrLn greeting
name <- getContents
names <- readFile nameFile
putStrLn (frankJ name names)
together with the 'pure' terms:
greeting, nameFile :: String
greeting = "What is your name?"
nameFile = "names.txt"
Either way, we are now really in Haskell-land: the problem is now to figure out what the pure function:
frankJ :: String -> String -> String
should be.
We might start with a simple matching function: we get a match when the first string appears on a list of strings:
match :: String -> [String] -> Bool
match name namelist = name `elem` namelist
-- pretty clever, that!
or we might want to normalize a bit, so that white space at the beginning and end of the name we are given and the names on the list doesn't affect the match. Here's a rather shabby way to do that:
clean :: String -> String
clean = reverse . omitSpaces . reverse . omitSpaces
where omitSpaces = dropWhile (== ' ')
Then we can improve on our old match, i.e. elem:
matchClean :: String -> [String] -> Bool
matchClean name namelist = match (clean name) (map clean namelist)
Now we need to follow the types, figuring out how to fit the type of, say, matchClean:: String -> [String] -> Bool with that of frankJ :: String -> String -> String. We want to fit it inside our definition of frankJ.
Thus, to 'provide input' for matchClean, we need a function to take us from a long string with newlines to the list of stings (the names) that matchClean needs: that's the Prelude function lines.
But we also need to decide what to do with the Bool that matchClean yields as value; frankJ, as we have it, returns a String. Let us continue with simple-minded decomposition of the problem:
response :: Bool -> String
response False = "We're sorry, your name does not appear on the list, please leave."
response True = "Hey, you're on the A-list, welcome!"
Now we have materials we can compose into a reasonable candidate for the function frankJ :: String -> String -> String that we are feeding into our IO machine defined in main:
frankJ name nametext = response (matchClean name (lines nametext))
-- or maybe the fancier:
-- frankJ name = response . matchClean name . lines
-- given a name, this
-- - pipes the nametext through the lines function, splitting it,
-- - decides whether the given name matches, and then
-- - calculates the 'response' string
So here, almost everything is a matter of pure functions, and it is easy to see how to emend things for further refinement. For example, maybe the name entered and the lines of the text file should be further normalized. Internals spaces should be restricted to one space, before the comparison. Or maybe there is a comma in lines on the list since people are listed as "lastname, firstname", etc. etc. Or maybe we want the response function to use the person's name:
personalResponse :: String -> Bool -> String
personalResponse name False = name ++ " is a loser, as far as I can tell, get out!"
personalResponse name True = "Ah, our old friend " ++ name ++ "! Welcome!"
together with
frankJpersonal name = personalResponse name . matchClean name . lines
Of course there are a million ways of going about this. For example, there are regex libraries. The excellent and simple Data.List.Split from Hackage might also be of use, but I'm not sure it can be used by Hugs, which you might be using.
I note that you are using old-fashioned names for the imported modules. What I have written uses only the Prelude so imports are unnecessary, but the other modules are now called "System.IO", "Data.List" and "Control.Monad" in accordance with the hierarhical naming system. I wonder if you are using an old tutorial or manual. Maybe the pleasant 'Learn You a Haskell' site would be better? He affirms he's using ghc but I think that won't affect much.
If you wan't a list of all lines in your list.txt that contain the name,
you can simply use
filter (isInfixOf name) list
but I'm not sure if I understood your question correct.