1
votes

Learning purescript. I try to use Data.Foldable (find) on a list of filenames with Data.String.Regex (test). test needs a Regex as input, but Data.String.Regex (regex) retuns (or creates?) result (or value? or function?) Either String Regex.

The problem is: for two days I can not find any example of dealing with Either in purescript and how to incorporate it in complex expresions (can not understand how to mix and match several functions and their output results together).

I want to do something like: searched_item = find test regex """^.*$""" noFlags ns But all my experiments fail to compile, result in errors.

I tryed to look for Haskel/Elm examples, but as I understood their regex implementations return Regex (not Either String Regex) and can be used stright away.

I understand that Either is there for error handling if regex will fail to parse string to regex, and that I need to somehow use Left and Right. I just cant get the rules from documentation or find a comprehensible begginer-level example.

EDIT: One experiment with Regex that actualy compiles is:

re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags
str :: String
str = "1_sh_2_3a.csv"
f :: Either String Regex -> String -> Boolean
f r s = case r of
  Left _ -> false
  Right r' -> test r' s
res :: Boolean
res = f re str

and the res is true. (Btw! Is there a way to glue it all together in less lines?)

But how do I stick it into something like:

main = do
  ns <- readdir "."
  for ns log
  -- TODO: find and log a fliename
1
Would recommend to take a look at the PureScript book leanpub.com/purescript/readSafareli

1 Answers

2
votes

One perfectly viable alternative is to screw the safety: if your regular expression is known at compile time (i.e. doesn't come from the user or the database or some such), and you know for sure that it's correct, you can just "swear" to the compiler that the result will never be Left. To do this, use the standard function fromRight, which is partial (i.e. sometimes crashes), so you'll need to wrap it in unsafePartial to prevent a compiler warning:

re :: Regex
re = unsafePartial $ fromRight $ regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

This will crash if the regex turns out to be malformed somehow, so be careful.


If you don't want to give up the safety like that, then what you have done is more or less it, except of course you can factor it a bit better.

The first thing to know is operators <$>, <*>, and <@>, all usually referred to as "splat". Their purpose in life is to take a function and apply it to the "inside" of some container. They work with any sort of thing that has a "value inside" - and Either is one of those.

So, for example, this works:

a :: Either String Int
a = Right 40

plus2 :: Int -> Int
plus2 x = x + 2

b :: Either String Int
b = plus2 <$> a

-- Now b == Right 42

(for future reference, a thing that has a "value inside" is generally referred to as "functor")

You can also do this for functions with multiple parameters, for example (omitting type signatures for brevity):

a = Right 40
b = Right 2
plus x y = x + y
c = plus <$> a <*> b
-- Now c == Right 42

Note that in order to use operator <*>, the second argument must also be "inside a functor". If your second argument happens to be on its own, without a functor, use operator <@>:

a = Right 40
b = 2
c = plus <$> a <@> b

Now, armed with this knowledge, we can implement your testing function like so:

f r s = test <$> r <@> s

But of course, now the function f returns an Either String Boolean - i.e. the resulting Boolean remains wrapped in an Either.

So in order to get that value out of Either, you need a function Either String Boolean -> Boolean, or, more generally, you can look at such function as b -> Either a b -> b, with the semantics of "if the value is Right, return it, otherwise return the provided fallback value". It would be implemented something like this:

ifLeft :: forall a b. b -> Either a b -> b
ifLeft _ (Right b) = b
ifLeft b (Left _) = b

For some reason that I don't quite understand, such function is not present in any of the "standard" PureScript libraries (it does exist in Haskell though - fromRight). So unfortunately you'll have to include your own implementation, or you can opt for using some of the existing combinators, for example:

-- Alternative option 1
ifLeft b = either (const b) identity

-- Alternative option 2
ifLeft b = fromMaybe <<< hush b

Tying all of the above together, your program would look something like this:

re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

ifLeft :: forall a b. b -> Either a b -> b
ifLeft _ (Right b) = b
ifLeft b (Left _) = b

res :: Boolean
res = ifLeft false $ test <$> re <@> "1_sh_2_3a.csv"

Granted, this is not much shorter, but now you can reuse the ifLeft function later, for something different. Or, alternatively, you can perform some more complex computation all within the Either wrapper, and unwrap it only at the very end.

Or, alternatively, replacing the explicit definition of ifLeft with a call to the standard combinator either:

re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

res :: Boolean
res = either (const false) identity $ test <$> re <@> "1_sh_2_3a.csv"