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"