Split a string by a chosen character in haskell - haskell

I'm trying to split a string every time there is a chosen character. So if I receive "1,2,3,4,5", and my chosen character is "," the result is a list such as ["1","2","3","4","5"].
I've been looking through the already answered questions in here and they point me to using splitOn. However, when i try to import Data.List.Split in order to use it, Haskell gives me the following error: Could not find module ‘Data.List.Split’ . When I tried to just use splitOnwithout importing the module, it showed me Variable not in scope: splitOn.
So my questions are,
Is it normal that i'm getting this error? Is splitOn a viable option or should I just try something else?
What other simple solutions are there?
I can just write something that will do this for me but I'm wondering why I'm not able to import Data.List.Split and if there are other simpler options out there that I'm not seeing. Thank you!

If you're using GHC it comes with the standard Prelude and the modules in the base package, and perhaps a few other packages.
Most packages, like the split package (which contains the Data.List.Split module), aren't part of GHC itself. You'll have to import them as an explicit compilation step. This is easiest done with a build tool. Most Haskellers use either Cabal or Stack.
With Stack, for example, you can add the split package to your package.yaml file:
dependencies:
- base >= 4.7 && < 5
- split
You can also load an extra package when you use Stack to start GHCi. This is useful for ad-hoc experiments.

‘Data.List.Split’ is not in prelude and needs to be installed as a dependency package.
Install command depends on environment you are using:
‘stack install split’ for stack
‘cabal install split’ for cabal

Basically this is a foldring job. So you may simply do like
λ> foldr (\c (s:ss) -> if c == ',' then "":s:ss else (c:s):ss) [""] "1,2,3,42,5"
["1","2","3","42","5"]
So;
splitOn x = foldr (\c (s:ss) -> if c == x then "":s:ss else (c:s):ss) [""]
However this will give us reasonable but perhaps not wanted results such as;
λ> splitOn ',' ",1,2,3,42,5,"
["","1","2","3","42","5",""]
In this particular case it might be nice to trim the unwanted characters off of the string in advance. In Haskell though, this functionality i guess conventionally gets the name
dropAround :: (Char -> Bool) -> String -> String
dropAround b = dropWhile b . dropWhileEnd b
λ> dropAround (==',') ",1,2,3,42,5,"
"1,2,3,42,5"
accordingly;
λ> splitOn (',') . dropAround (==',') $ ",1,2,3,42,5,"
["1","2","3","42","5"]

Related

Can not use spltOn to split String on spaces

I have a string containing words separated by spaces. I would like to get the individual words.
I verified (in ghci) that the following solution works well:
import Data.String
list_of_words = words "a b c"
but I happened to stumble accross the splitOn function, and since I am still in the basement of Haskell programming, and a curious mind, I wondered whether I could also achieve my goal using this function. Hence I tried:
import Data.List
lw = Data.List.splitOn "a b c"
only to get the error message
Not in scope: ‘Data.List.splitOn’
No module named ‘Data.List’ is imported
I don't understand the first message, but the second one is ridiculous, since I imported the module, and the module name is shown in the ghci prompt. Then I realized that the website above referst to a module Data.List.Split, not Data.List. Hence I tried
import Data.List.Split
only to get the error messages
Could not find module ‘Data.List.Split’
Well, curiosity killed the cat. I should have stayed with my original words solution. But since I opened the box now, could some kind soul explain to me, why my attempts have failed, and how I could have it done correctly?
UPDATE Following the comments to my question, I tried
splitOn " " "a c d"
but this time get the error message
Variable not in scope: splitOn :: [Char] -> [Char] -> t
Perhaps you meant one of these:
‘Text.splitOn’ (imported from Data.Text),
‘splitAt’ (imported from Data.List),
‘Text.splitAt’ (imported from Data.Text)
It looks like Data.Text (which I happened to import too) also has a splitOn function with different signature. Hence, I started a fresh ghci without any imports, and just did a
import Data.List
splitOn " " "a b c"
This gave also the error message
Variable not in scope: splitOn :: [Char] -> [Char] -> t
Install the split library to be able to use it. Since you're a stack user here are the commands for that:
stack build split # install split (some people prefer "stack build" over "stack install", but the difference concerns only executables)
stack ghci # start ghci using the environment of packages installed by stack

Haskell: Parse error: module header, import declaration or top-level declaration expected

I am saving some commands in a Haskell script in a .hs file while working thru a Haskell textbook. Here's a small example.
fst (1,2)
snd (1,2)
When I run these commands from the prelude in GHCi, they work fine. When I try to compile the .hs file with these two lines, I get the following:
ch4_test.hs:2:1: error:
Parse error: module header, import declaration
or top-level declaration expected.
|
2 | fst (1,2)
| ^^^^^^^^^
Failed, no modules loaded.
I've googled this error and can't find any explanation what I'm doing wrong.
From a newbie to future newbies: The interactive environment ghci would lead you to believe that you can punch some expressions into an .hs file and run the thing (in a similar fashion to languages like swift and ruby). This is not the case.
Haskell needs an entrypoint called main. Quoting:
Here is a simple program to read and then print a character:
main :: IO ()
main = do c <- getChar
putChar c
The use of the name main is important: main is defined to be the entry point of a Haskell program (similar to the main function in C), and must have an IO type, usually IO ()
Source: https://www.haskell.org/tutorial/io.html
You can't just put any expression in a hs file.
As the error message says, you need a declaration here. For example:
main =
print (fst (1,2)) >>
print (snd (1,2))
I am getting this error but the cause appears to be completely different from anything posted here. And the error message is not at all helpful.
Using Cabal version 3.6.2.0 with GHCI 8.10.7 on MacOS High Sierra (10.13)
I'm working from this page: https://www.tutorialspoint.com/haskell/haskell_modules.htm
specifically the "custom modules" section. There you can see the code I copied and pasted.
Besides the tutorial not mentioning I needed to add "other-modules: Custom" to myfirstapp.cabal, and besides the fact that the sample Custom.hs file includes "if x 'rem' 2 == 0" rather than "if x rem 2 == 0", here is the problem:
Indentation matters!
This line (inside the quotes) does NOT work "if x rem 2 == 0".
This line DOES work " if x rem 2 == 0"!
Indenting by one space is the difference between success and failure.
I'm totally new to Haskell. I've programmed extensively in PHP, Javascript, and Applescript, and dabbled in a dozen others, and this is the first time I've seen white space matter. I assume this is commonly known amongst Haskell veterans, but it would certainly be nice if that was included prominently in the documentation.

Using Emoji in Haskell

I've recently come across a bot on Twitter named EmojiHaskell, that claims to tweet 'interpretable Haskell code with emoji variable names'. A particular Tweet caught my attention, as it looked like malformed syntax to me, so I decided to take a closer look. So far I've produced the following code:
module Main where
🙏 :: [🍳] -> Maybe 🍳
🙏 [] = Nothing
🙏 (👽:as) = Just 👽
main = print $ 🙏 "♥"
Since I've used λ on occasion in my Haskell code, I expected this code to work, but it appears that GHC doesn't like the emoji at all.
With $ runhaskell Main.hs I get:
Main.hs:4:1: parse error on input ‘🙏’
I've already had a look at the UnicodeSyntax extension,
and tried to only use some or single emoji instead of all of them to see if a certain one provokes the problem.
Now my question is this:
Is there currently a Haskell compiler that would accept the code?
Can I get GHC to work with this code somehow?
That code is not valid haskell. The reason is that 🙏 (like, probably, all Emojis) is a symbol character:
Prelude> import Data.Char
Prelude Data.Char> generalCategory '🙏'
OtherSymbol
But you can still use them like any other symbol, namely as an operator:
Prelude Data.Char> let (🙏) = (+)
Prelude Data.Char> 32 🙏 42
74
Furthermore, as user3237465 pointed out, if you use the prefix syntax for operators, i.e. put it in parentheses, you can even use it like any other symbol:
(🙏) :: [a] -> Maybe a
(🙏) [] = Nothing
(🙏) ((👽):as) = Just (👽)
main = print $ (🙏) "♥"
This is almost the example in the original post. Unfortunately, this trick does not work for the type variable. The the documentation is worded a bit unfortunately, but in fact symbols are never type variables and always type constructors

Just and dot in winghci

Why does this work...
Just.(+3) $ 6.7
Just $ truncate 8.9
...but not this?
Just.truncate $ 8.9
I tried resolving truncate to a simple Double -> Int:
let f :: Double -> Int; f = (\ x -> truncate x);
...but that doesn't appear to be the problem...
Just.f $ 5.6
<interactive>:41:1:
Failed to load interface for `Just'
Use -v to see a list of the files searched for.
Many thanks!
When you mean to compose functions, it's better to write f . g than f.g. It's a little more readable, and you avoid a bunch of problems like this one.
When you have something of the form Foo.bar or Foo.Bar in Haskell, it is parsed as a qualified name. That's why Just.f doesn't work: Just isn't a module, so the 'interface' for Just can't be loaded.
Why Just.(+3) does work as intended: (+3) is a right section, not an identifier, so the dot can't be part of a qualified name. The only way to interpret it is to assume that . is an infix application of the operator (.), so it must be Just . (+3).
A dot between a capitalized identifier and another identifier is parsed as a qualified name (eg. Data.Map.insert), so the error is telling you that it couldn't find a module named Just. You can simply add spaces around the dot to fix this.

Alpha conversion on a Haskell expression

Given a Haskell expression, I'd like to perform alpha conversion, ie. rename some of the non free variables.
I've started implementing my own function for this, which works on a haskell-src-exts Exp tree, however it turns out to be surprisingly nontrivial, so I can't help wondering - is there an established easy-to-use library solution for this kind of source conversion? Ideally, it should integrate with haskell-src-exts.
This is one of the problems where the "Scrap Your Boilerplate" style generic libraries shine!
The one I'm most familiar with is the uniplate package, but I don't actually have it installed at the moment, so I'll use the (very similar) functionality found in the lens package. The idea here is that it uses Data.Data.Data (which is the best qualified name ever) and related classes to perform generic operations in a polymorphic way.
Here's the simplest possible example:
alphaConvert :: Module -> Module
alphaConvert = template %~ changeName
changeName :: Name -> Name
changeName (Ident n) = Ident $ n ++ "_conv"
changeName n = n
The (%~) operator is from lens and just means to to apply the function changeName to everything selected by the generic traversal template. So what this does is find every alphanumeric identifier and append _conv to it. Running this program on its own source produces this:
module AlphaConv where
import Language.Haskell.Exts
import Control.Lens
import Control.Lens.Plated
import Data.Data.Lens
instance Plated_conv Module_conv
main_conv
= do ParseOk_conv md_conv <- parseFile_conv "AlphaConv.hs"
putStrLn_conv $ prettyPrint_conv md_conv
let md'_conv = alphaConvert_conv md_conv
putStrLn_conv $ prettyPrint_conv md'_conv
alphaConvert_conv :: Module_conv -> Module_conv
alphaConvert_conv = template_conv %~ changeName_conv
changeName_conv :: Name_conv -> Name_conv
changeName_conv (Ident_conv n_conv)
= Ident_conv $ n_conv ++ "_conv"
changeName_conv n_conv = n_conv
Not terribly useful since it doesn't distinguish between identifiers bound locally and those defined in an outside scope (such as being imported), but it demonstrates the basic idea.
lens may seem a bit intimidating (it has a lot more functionality than just this); you may find uniplate or another library more approachable.
The way you'd approach your actual problem would be a multi-part transformation that first selects the subexpressions you want to alpha-convert inside of, then uses a transformation on those to modify the names you want changed.

Resources