So I got it into my head to learn me some Haskell (boriing night shifts), and I put together a program that can ease my vacation planning by being able to calculate my shift for any giving period.
import System.Environment
import Data.List
import Data.List.Split
import Data.Time
import Data.Time.Calendar.WeekDate (toWeekDate)
-- merge xs and ys lists alternating value from each [ xs(0),ys(0),(xs(1),ys(1)..xs(n),ys(n) ]
merge :: [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) = x : y : merge xs ys
-- get part of list from index 'start' to 'stop'
slice :: Int -> Int -> [a] -> [a]
slice start_from stop_at xs = fst $ splitAt (stop_at - start_from) (snd $ splitAt start_from xs)
timeFormat = "%d-%m-%Y"
timeFormatOut :: Day -> String
timeFormatOut = formatTime defaultTimeLocale "%d-%m-%Y"
-- parses Strings to DateTime Day1
parseMyDate :: String -> Day
parseMyDate = parseTimeOrError True defaultTimeLocale timeFormat
-- 8 week shift rotation
shiftRotation = cycle ["NAT","NAT","NAT","NAT","NAT","NAT","NAT","-","-","-","-","-","-","-","DAG","DAG","DAG","DAG","-","AFT","AFT","-","-","DAG","DAG","DAG","-","-","DAG","DAG","DAG","-","DAG","DAG","DAG","DAG","DAG","-","DAG","DAG","-","-","AFT","AFT","AFT","AFT","AFT","-","-","DAG(r)","DAG(r)","DAG(r)","DAG(r)","DAG(r)","(r)","(r)"]
hs_findshift :: String -> String -> String -> IO String
hs_findshift anchor start end = do
let dayZero = parseMyDate anchor
let startDate = parseMyDate start
let endDate = parseMyDate end
let startPos = fromIntegral (diffDays startDate dayZero)
let endPos = fromIntegral (diffDays endDate dayZero) + 1
let period = slice startPos endPos shiftRotation
let dates = map (timeFormatOut) [startDate..endDate]
let listStr = (concat(intersperse "," (merge dates period)))
putStrLn listStr
This works nicely. Now I throught I'd try and export it to a C# app so I could make a nice interface. I'm having some trouble with the it. I added
module Shiftlib where
import Foreign.C.String
import Foreign.C.Types
to the top. Right under the imports I added a block to convert inputs and outputs to C types.
foreign export ccall findshift :: CString -> CString -> CString -> IO CString
findshift :: CString -> CString -> CString -> IO CString
findshift a s e = do
anchor <- peekCString a
start <- peekCString s
end <- peekCString e
result <- hs_findshift anchor start end
return $ newCString result
now it doesn't compile. It seems that "return $ newCString result" returns an IO ( IO CString ) which the "foreign" call wont accept.
:l shiftlib
[1 of 1] Compiling Shiftlib ( shiftlib.hs, interpreted )
shiftlib.hs:53:1: error:
* Illegal foreign declaration: requires unregisterised, llvm (-fllvm) or native code generation (-fasm)
* When checking declaration:
foreign export ccall "findshift" findshift
:: CString -> CString -> CString -> IO CString
|
53 | foreign export ccall findshift :: CString -> CString -> CString -> IO CString
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
shiftlib.hs:61:5: error:
* Couldn't match type `IO CString' with `GHC.Ptr.Ptr CChar'
Expected type: IO CString
Actual type: IO (IO CString)
* In a stmt of a 'do' block: return $ newCString result
In the expression:
do anchor <- peekCString a
start <- peekCString s
end <- peekCString e
result <- hs_findshift anchor start end
....
In an equation for `findshift':
findshift a s e
= do anchor <- peekCString a
start <- peekCString s
end <- peekCString e
....
|
61 | return $ newCString result
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
I cant seem to get around it. How can I return a CString from my little module?
newCString :: String -> IO String
result :: String
newCString result :: IO String
return :: a -> IO a
return $ newCString result :: IO (IO String)
Simply stop when you already have the thing you want, newCString result.
When you work inside the IO monad, in a function of type foo :: .. -> .. -> IO T
the last entry in a do block must have type IO T.
return exists so that, if you only have T, you can wrap it inside IO. Essentially, an expression of type T evaluates to a value of type T without any side effects (e.g. printing messages, or mutating IORefs). return turns that value into an IO computation of type IO T. The type IO T is for expressions that could have side effects before returning T. return creates a computation that does no side effects, as a trivial subcase of IO T.
newCString result is already of type IO CString, so if you use return you get it wrapped too many times, as something of type IO (IO CString). This is a computation which does not return a string, but another computation to run (which will eventually produce a string).
The obvious solution here is to remove the return to avoid the redundant wrapping.
Another, not recommended, solution would be
do ...
...
string <- newCString result
return string
This would work since string would have type String, not IO CString. This is not recommended because it's redundant. Indeed, we could even add more redundancy as
do ...
...
string <- newCString result
string2 <- return string
string3 <- return string2
return string3
which would have the same effect. But this serves no purpose, and impacts readability.
Your code is perfectly fine as it is. Perhaps you also want to consider some alternatives using the "applicative style".
findshift :: CString -> CString -> CString -> IO CString
findshift a s e = do
result <- hs_findshift <$> peekCString a <*> peekCString s <*> peekCString e
newCString result
or even
findshift :: CString -> CString -> CString -> IO CString
findshift a s e =
hs_findshift <$> peekCString a <*> peekCString s <*> peekCString e >>= newCString
(I'm not a fan of this last one, though.)
Related
The problem sounds like this: write a program that reads a number n and then n persons, for each persons, read their name and age and then return the oldest persons/persons.
Example input:
3
Ion Ionel Ionescu
70
Gica Petrescu
99
Mustafa ben Muhamad
7
Example output
Oldest is Gica Petrescu (99 years).
My code so far:
readPers :: IO(String, Int)
readPers = do
name <- getLine
age <- readLn :: IO Int
return (name, age)
readPerss :: (Ord t, Num t) => t -> [IO (String, Int)]
readPerss n
| n > 0 = readPers : readPerss(n-1)
| otherwise = []
pFunc = do
print "Numer of persons:"
n <- readLn :: IO Int
let persons = readPerss n
return persons
I first read n, then I try to make a list of persons using readPers and readPerss, but I am stuck, I don't know how to tackle it from that point forward and I guess that my implementation thus far is not quite right.
How should I solve the problem?
You are very close! What you are doing in readPerss :: (Ord t, Num t) => t -> [IO (String, Int)] is returning a list of IO actions; each action returns a pair of String and Int when it is executed. Currently in pFunc you are only building this list of actions, storing it in a variable with let, and returning it from pFunc; you are never executing them with a <- “bind” statement.
There are a few simple ways to do what you want. The smallest change to your code that does what you want is to add sequence, which takes a container of actions and produces an action that returns a container:
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
Here t is [], m is IO, and a is (String, Int):
sequence :: [IO (String, Int)] -> IO [(String, Int)]
Another way is to rewrite readPerss so that it executes the actions directly, accumulating the (String, Int) results in a list instead of accumulating the IO actions:
readPerss :: (Ord t, Num t) => t -> IO [(String, Int)]
-- Change [IO …] to IO […]: ~~~~~~~~~~~~~~~~~~
readPerss n
| n > 0 = do
pers <- readPers
perss <- readPerss (n - 1)
return (pers : perss)
| otherwise = return []
I know you may not be supposed to use library functions if this is a homework assignment or exercise, but in typical code “repeat x action n times and accumulate the results” is often represented with replicateM n x:
replicateM :: Applicative m => Int -> m a -> m [a]
This is how I always do this (it is from a code challenge isn’t it). I always seperate IO and logic as soon as possible. Works perfect (unless N is very big).
import Data.List.Split (chunksOf)
type Person = (String, Int)
main = do
x <- getContents
putStrLn $ program x
program :: String -> String
program s = “Oldest is “ ++ x ++ “ (“ ++ (show y) ++ “ years old).”
where
(x, y) = solve persons
persons = [(name, read age :: Int) | [name, age] <- chunksOf 2 . tail . lines $ s]
solve :: [Person] -> Person
solve ls = undefined
I leave the undefined to you.
I'm trying to create a function for a silly IRC bot that will return a phrase where some of the letters are repeated a random number of times. The problem I'm having is that I can't find a way to use random numbers that ghc likes. It seems that even using this answer isn't being particularly helpful for getting my code to compile.
import System.Random
-- Write bad
baaad x y = "B" ++ (repeatA x) ++ "D " ++ (exclaim y)
-- StartHere
randomBad :: String
randomBad = do
x <- randomRIO(5,10) :: IO Int
y <- randomRIO(0,6) :: IO Int
return $ baaad x y
repeatA :: Int -> String
repeatA x = rptChr "A" x
exclaim :: Int -> String
exclaim x = rptChr "!" x
rptChr :: String -> Int -> String
rptChr x y = take y (cycle x)
Even with the trick of using a do block and passing the IO Ints to the function that way, I'm still getting compile errors that it found an IO Int when expecting Int.
randomBad is not in the IO monad.... It is type String, but you are defining it to be type IO String
Change this
randomBad :: String
to this
randomBad :: IO String
Then you should be able to use this in another IO action, like main:
main = do
theString <- randomBad
putStrLn theString
I am a beginner with haskell and i wonder how i can count the characters in a file in haskell. From this book i wrote the count of any character in any string, but i wonder if i can do the same function with files. The code is similar like following;
count :: Char -> String -> Int
count x xs = length [x'|x'<-xs, x==x']
Any help would be very great. Thanks!
Edit: I am very new to haskell so this may be a very stupid question :)
By reusing your count function. You can map it over a readFile with returns you a file's content as a string:
count :: Eq a => a -> [a] -> Int
count x xs = length [x' | x' <- xs, x==x']
cntFile :: Char -> FilePath -> IO Int
cntFile c f = count c <$> readFile f
main :: IO ()
main = do
cnt <- cntFile 'c' "test.hs"
print cnt
The operator <$> is just the infix notation for fmap, which does to IO and any other Functor what map does to lists.
The longer alternative for beginner would probably be:
cntFile' :: Char -> FilePath -> IO Int
cntFile' c f = do
content <- readFile f
let cnt = count c content
return cnt
I have a problem with a Haskell script. I'm trying to learn Haskell through doing problems I find online. The input I get is:
Int -> Number of test cases
S1 -> String 1 for each test case
S2 -> String 2 for each test case
Each S1 and S2 is a space delimited string of numbers. I convert them to a list of Ints with the function strToIntList. I then want to process the two lists and return an integer value based on the results. I get the following error: Couldn't match type 'IO Integer' with 'Int' on line 24 and I stared for ages but just can't figure out why (full error at end of post).
If someone could explain why I'm going wrong, I would be very grateful.
This is my script:
import Data.List
import Data.List.Split
main = do
cases <- readLn
doLoop cases
toInt x = read x :: Int
strToIntList s = [read x :: Int | x <- (splitOn " " s)]
minOfTwo :: Int
minOfTwo = do
sa <- getLine
sb <- getLine
return $ minimum [1..50]
-- return $ minimum $ strToIntList sa
doLoop 0 = return ()
doLoop loopIndex = do
q <- getLine
let c = minOfTwo
print(c)
doLoop (loopIndex-1)
This is the full error I'm getting:
Couldn't match type `IO Integer' with `Int'
Expected type: IO String -> (String -> IO Integer) -> Int
Actual type: IO String -> (String -> IO Integer) -> IO Integer
In a stmt of a 'do' block: sa <- getLine
In the expression:
do { sa <- getLine;
sb <- getLine;
return $ minimum [1 .. 50] }
In an equation for `minOfTwo':
minOfTwo
= do { sa <- getLine;
sb <- getLine;
return $ minimum [1 .. 50] }
The getLine function is in the IO monad, and therefore any function that calls getLine must also be in the IO monad. Change your type signature for minOfTwo from Int to IO Int, and that particular problem will go away.
(You'll also need to change let c = minOfTwo into c <- minOfTwo.)
There may be other errors, but this is the one causing your error message.
I have the following code:
import System.Environment
import System.Directory
import System.IO
import Data.List
dispatch :: [(String, [String] -> IO ())]
dispatch = [ ("add", add)
, ("view", view)
, ("remove", remove)
, ("bump", bump)
]
main = do
(command:args) <- getArgs
let result = lookup command dispatch
if result == Nothing then
errorExit
else do
let (Just action) = result
action args
errorExit :: IO ()
errorExit = do
putStrLn "Incorrect command"
add :: [String] -> IO ()
add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
view :: [String] -> IO ()
view [fileName] = do
contents <- readFile fileName
let todoTasks = lines contents
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
putStr $ unlines numberedTasks
remove :: [String] -> IO ()
remove [fileName, numberString] = do
handle <- openFile fileName ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let number = read numberString
todoTasks = lines contents
newTodoItems = delete (todoTasks !! number) todoTasks
hPutStr tempHandle $ unlines newTodoItems
hClose handle
hClose tempHandle
removeFile fileName
renameFile tempName fileName
bump :: [String] -> IO ()
bump [fileName, numberString] = do
handle <- openFile fileName ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let number = read numberString
todoTasks = lines contents
bumpedItem = todoTasks !! number
newTodoItems = [bumpedItem] ++ delete bumpedItem todoTasks
hPutStr tempHandle $ unlines newTodoItems
hClose handle
hClose tempHandle
removeFile fileName
renameFile tempName fileName
Trying to compile it gives me the following error:
$ ghc --make todo
[1 of 1] Compiling Main ( todo.hs, todo.o )
todo.hs:16:15:
No instance for (Eq ([[Char]] -> IO ()))
arising from a use of `=='
Possible fix:
add an instance declaration for (Eq ([[Char]] -> IO ()))
In the expression: result == Nothing
In a stmt of a 'do' block:
if result == Nothing then
errorExit
else
do { let (Just action) = ...;
action args }
In the expression:
do { (command : args) <- getArgs;
let result = lookup command dispatch;
if result == Nothing then
errorExit
else
do { let ...;
.... } }
I don't get why is that since lookup returns Maybe a, which I'm surely can compare to Nothing.
The type of the (==) operator is Eq a => a -> a -> Bool. What this means is that you can only compare objects for equality if they're of a type which is an instance of Eq. And functions aren't comparable for equality: how would you write (==) :: (a -> b) -> (a -> b) -> Bool? There's no way to do it.1 And while clearly Nothing == Nothing and Just x /= Nothing, it's the case that Just x == Just y if and only if x == y; thus, there's no way to write (==) for Maybe a unless you can write (==) for a.
There best solution here is to use pattern matching. In general, I don't find myself using that many if statements in my Haskell code. You can instead write:
main = do (command:args) <- getArgs
case lookup command dispatch of
Just action -> action args
Nothing -> errorExit
This is better code for a couple of reasons. First, it's shorter, which is always nice. Second, while you simply can't use (==) here, suppose that dispatch instead held lists. The case statement remains just as efficient (constant time), but comparing Just x and Just y becomes very expensive. Second, you don't have to rebind result with let (Just action) = result; this makes the code shorter and doesn't introduce a potential pattern-match failure (which is bad, although you do know it can't fail here).
1:: In fact, it's impossible to write (==) while preserving referential transparency. In Haskell, f = (\x -> x + x) :: Integer -> Integer and g = (* 2) :: Integer -> Integer ought to be considered equal because f x = g x for all x :: Integer; however, proving that two functions are equal in this way is in general undecidable (since it requires enumerating an infinite number of inputs). And you can't just say that \x -> x + x only equals syntactically identical functions, because then you could distinguish f and g even though they do the same thing.
The Maybe a type has an Eq instance only if a has one - that's why you get No instance for (Eq ([[Char]] -> IO ())) (a function can't be compared to another function).
Maybe the maybe function is what you're looking for. I can't test this at the moment, but it should be something like this:
maybe errorExit (\action -> action args) result
That is, if result is Nothing, return errorExit, but if result is Just action, apply the lambda function on action.