0

I am attempting to write my own function which takes an arbitrary number of tokens, and then splits an arbitrary string on any of those.

After a little bit of thinking, I believe I need to recursively iterate through a list of tokens, and then pass each split list into a map with the splitting function, followed by a flatten.

At present, my algorithm looks like so:

module MyAwesomeModule where

import qualified Data.Text as T

outputSplit :: String -> [String] -> IO ()
outputSplit s tokens = print $ splitRecursive tokens s

splitRecursive :: [String] -> String -> [String]
splitRecursive tokens s = splitOneOf tokens s

splitOneOf :: [String] -> String -> [String]
splitOneOf [] s = []
splitOneOf (t:tokens) s =  map (splitOneOf tokens)(map (T.unpack) (T.splitOn (T.pack t) (T.pack s))) ++ (splitOneOf tokens s)

Which errors out with:

Couldn't match type `[Char]' with `Char'
Expected type: String -> String
  Actual type: String -> [String]
In the return type of a call of `splitOneOf'
In the first argument of `map', namely `(splitOneOf tokens)'
In the first argument of `(++)', namely
  `map
     (splitOneOf tokens)
     (map (T.unpack) (T.splitOn (T.pack t) (T.pack s)))'

So as far as I understand, what that means is that the Strings in the initial split are being cast to [Char]

   Prelude > let a = (map (T.unpack) (T.splitOn (T.pack "a") (T.pack "abcdefabc")))
             ["","bcdef","bc"]
             :t a
             a::[String]
             let b = head a
             :t b
             b::String

Moreover, if splitOneOf is defined as:

    splitOneOf :: [String] -> String -> [String]
    splitOneOf [] s = []
    splitOneOf (t:tokens) s =  (map (T.unpack) (T.splitOn (T.pack t) (T.pack s))) ++ (splitOneOf tokens s)

then

   Prelude > let a = splitOneOf ["a", "b"] "abcdefghij"
             ["", "bcdefghij"]
             map (splitOneOf ["b"]) a
             [[""], [[""],["cdefghij"]]

What exactly is going on with the type signatures here? Is this the right way to map? What am I missing?

12
  • 1
    Why does map have three arguments? Did you mean to put splitOneOf tokens in parantheses? Commented Mar 26, 2014 at 3:38
  • thanks for pointing that out, I've modified the question accordingly Commented Mar 26, 2014 at 3:47
  • There's no need to use Data.Text. There is Data.List.Split. Commented Mar 26, 2014 at 3:51
  • that's kinda neat. But say I wanted to roll my own for practice purposes? How would I map a function onto itself? Commented Mar 26, 2014 at 4:48
  • 1
    You want to use concatMap and splitOn to get something of type String -> [String] -> [String] (first argument is a token). Then use a common recursion encapsulating function to repeatedlt apply this to a list if tokens. Commented Mar 26, 2014 at 4:52

1 Answer 1

1
import Control.Monad ((>=>))
import Data.List.Split (splitOn)

--|Using list monad, for repeated concatMaps
splitOnMany :: [String] -- ^ delimiters
            -> String   -- ^ input
            -> [String] -- ^ output
splitOnMany [] = return
splitOnMany (d:ds) = splitOn d >=> splitOnMany ds

This list monad is usually thought of as some sort of a poor man's non-determinism or logic monad. But, any time you need to do recursive flattening of lists, it can be used. Here, we split on the first delimiter, then split all of those on the second delimiter, etc. and then flatten everything. I'm pretty sure the explicit recursion isn't really necessary either. The following is more generic, and might even optimize better:

import Control.Monad ((>=>))
import Data.List.Split (splitOn)

splitOnMany :: Eq a => [[a]] -> [a] -> [[a]]
splitOnMany = foldr (>=>) return . map splitOn
Sign up to request clarification or add additional context in comments.

2 Comments

I think the list monad monad only obscures the matters here, it's really just splitonMany tokens line = foldr (concatMap . splitOn) [line] tokens.
For yours, why not foldl'? For mine, the additional strictness doesn't help. I think mine inlines better, since it is point-free. You are right that the Kleisli fish probably isn't too clarifying; maybe I've just been thinking in monads too much lately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.