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
Related
I was wondering, how can we do multiple things in a Haskell function?
For example, I have this code:
number = 0
increment :: Int -> Int
increment i = i + 1
function :: String -> String
function s = s ++ " world" AND increment number
I was wondering, how can we do something like this? I'm searching everywhere but I don't find a solution for this simple example :O
number
0
:function "hello"
"hello world" (AND the number = 1 now)
number
1
note: I know that AND is not a syntax in Haskell but I wanted to make you understand what I wanted to say :)
You can't modify variables in Haskell (they are not, in fact, variable). What you can do is return new values:
f (string, number) = (string ++ " world", number + 1)
which would be used like this:
Prelude> f ("hello", 0)
("hello world",1)
If you called this function with a variable instead of a literal, it would not change the value of that variable, because Haskell is designed to prevent that from happening:
Prelude> let n = 0
Prelude> f ("hello", n)
("hello world",1)
Prelude> print n
0
the problem is: in haskell you don't have "mutable states" like in other programming languages: so "number = 1" wouldn't work in terms of "setting a State"
you could combine them as a result:
increment :: Int -> Int
increment i = i + 1
stringCombine :: String -> String
stringcombine str = str ++ "world"
combine i str = (increment i, stringCombine str)
or if you have a function with a side effect (like print, putStrLn) you have to use the IO Monad:
doSideEffects :: Int -> String -> IO Int
doSideEffects i str = do
putStrLn $ str ++ "world"
return $ increment i
I am making a program that replaces stuff using the Esperanto X-System to Esperanto, so I need it to transform "cx" to "ĉ", "sx" to "ŝ", "gx" to "g", "jx" to "ĵ", and "ux" to "ŭ", and the same for uppercase letters.
Currently it converts "a" to "b", and "c" to "d". The method I am currently using will only work for replacing single character, not multiple characters. So how do I replace multiple characters (like "cx") instead of a single one (like "a")?
replaceChar :: Char -> Char
replaceChar char = case char of
'a' -> 'b'
'c' -> 'd'
_ -> char
xSistemo :: String -> String
xSistemo = map replaceChar
So currently "cats" will transform to "dbts".
As #AJFarmar pointed out, you are probably implementing Esperanto's X-system [wiki]. Here all items that are translated are digraphs that end with x, the x is not used in esperato itself. We can for example use explicit recursion for this:
xSistemo :: String -> String
xSistemo (x:'x':xs) = replaceChar x : xSistemo xs
xSistemo (x:xs) = x : xSistemo xs
xSistemo [] = []
where we have a function replaceChar :: Char -> Char, like:
replaceChar :: Char -> Char
replaceChar 's' = 'ŝ'
-- ...
This then yields:
Prelude> xSistemo "sxi"
"\349i"
Prelude> putStrLn (xSistemo "sxi")
ŝi
A generic method:
The problem looks similar to question 48571481.
So you could try to leverage the power of Haskell regular expressions.
Borrowing from question 48571481, you can use foldl to loop thru the various partial substitutions.
This code seems to work:
-- for stackoverflow question 57548358
-- about Esperanto diacritical characters
import qualified Text.Regex as R
esperantize :: [(String,String)] -> String -> String
esperantize substList st =
let substRegex = R.subRegex
replaceAllIn = foldl (\acc (k, v) -> substRegex (R.mkRegex k) acc v)
in
replaceAllIn st substList
esperSubstList1 = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
esperantize1 :: String -> String
esperantize1 = esperantize esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxii"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste
Program output:
st.a = abcxrsxdfuxoojxii
st.e = abĉrŝdfŭooĵii
We can shorten the code, and also optimize it a little bit by keeping the Regex objects around, like this:
import qualified Text.Regex as R
esperSubstList1_raw = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
-- try to "compile" the substitution list into regex things as far as possible:
esperSubstList1 = map (\(sa, se) -> (R.mkRegex sa, se)) esperSubstList1_raw
-- use 'flip' as we want the input string to be the rightmost argument for
-- currying purposes:
applySubstitutionList :: [(R.Regex,String)] -> String -> String
applySubstitutionList = flip $ foldl (\acc (re, v) -> R.subRegex re acc v)
esperantize1 :: String -> String
esperantize1 = applySubstitutionList esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxiicxtt"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste
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.)
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 data structure like this
data Something = Something Integer String String
and i want to convert
["Something", "3", "text", "42"]
to the data.
for now, i have
altRead :: Read a => [String] -> a
altRead = read . unwords . hack
where
hack = map (\x -> if isNumber x then x else "\"" ++ x ++ "\"")
isNumber = foldl (\b c -> isDigit c && b) True
but i forgot, that some numbers could be strings in the data structure.
is there a simple solution for this or do i need to write a alternative read typeclass?
You're writing a tiny parser atop some lexed tokens. You can't really implement a Read instance since read :: Read a => String -> a and you want to do [String] -> a for a == Something. You can take advantage of Read instances that already exist, though, to bootstrap parsing your Integer, for instance.
So let's try it. We'll parse a Something from the list of tokens.
import Safe -- gives us readMay :: Read a => String -> Maybe a
parseSomething :: [String] -> Maybe Something
parseSomething ("Something":strInt:stra:strb:_) =
do int <- readMay strInt
return $ Something int stra strb
parseSomething _ = Nothing
We could do it a little more compactly using Maybe as an Applicative, too
import Control.Applicative
parseSomething :: [String] -> Maybe Something
parseSomething ("Something":strInt:stra:strb:_) =
Something <$> readMay strInt <*> pure stra <*> pure strb
parseSomething _ = Nothing
Really, we should probably return any unconsumed tokens as well so we can continue parsing.
parseSomething :: [String] -> (Maybe Something, [String])
parseSomething ("Something":strInt:stra:strb:rest) =
(Something <$> readMay strInt <*> pure stra <*> pure strb, rest)
parseSomething rest = (Nothing, rest)
The reason I bring in all this structure to your parse is that this starts to head toward the space of parser combinators like Parsec. Whenever you've got a need for a complicated Read it begins to become useful to look at some of the really nice parsing libraries in Haskell.
With what you have, you don't really need to make it a typeclass. You can just do:
readSomething :: [String] -> Maybe Something
readSomething [_, n, s1, s2] = Just $ Something (read n) s1 s2
readSomething _ = Nothing
or, if you want to disambiguate on the first word:
data Something = Something Integer String String
| SomethingToo String Integer
readSomething :: [String] -> Maybe Something
readSomething ["Something", n, s1, s2] = Just $ Something (read n) s1 s2
readSomething ["SomethingToo", s, n] = Just $ SomethingToo s (read n)
readSomething _ = Nothing
In GHCI:
data Something = Something Integer String String deriving (Read, Show)
let somethingStrings = ["Something", "3", "text", "42"]
let escapeForSomething [a,b,c,d] = [a, b, "\""++c++"\"", "\""++d++"\""]
let something = read (unwords (escapeForSomething somethingStrings)) :: Something