I am writing some module in Erlang that is supposed to be compiled on various OTP versions. In some cases I would like to use the unsupported in older versions ++ operator pattern, but only if it is available. Therefore my attempt to this task goes as follows:
f([]) -> empty;
f([_|_]) -> cons;
-if(?OTP_RELEASE >= 23).
f([] ++ _) -> empty_append;
-endif.
f(X) -> error(nah).
The workarounds to this issue I found are:
Move the iffed clause to the beginning. This however does not work well when:
I need to add more -ifs
The order of clauses forbids me from doing so for any reason.
Create a beautiful garden of ifelses with whole function copied several times. Obviously, this does not pass.
Is there some convenient way to do it? If possible, I'd appreciate a general solution, not limited to the presented case.
From flow control in macros:
The macro directives cannot be used inside functions. So if you want to use -ifdef you need to copy several times the same function.
(I guess that it has to do with them being defined between - and .)
That being said, you can do something like:
f([]) -> empty;
f([_|_]) -> cons;
f([] ++ _) when ?OTP_RELEASE >= 23 -> empty_append;
f(X) -> error(nah).
You can verify that the clause that will never match is removed from the generated beam ASM code with erlc -S <module>. Also, the compiler will show a warning.
The warning generated during this step cannot be selectively omitted. From here:
Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as a=b), guards that always evaluate to false, and expressions that always fail (such as atom+42).
Those warnings cannot be disabled (except by disabling all warnings).
If you want to avoid the warning altogether, you need to something like ( note that this flavor will not delete the branch because OtpRelease is a variable ):
f(Term) ->
f(Term, ?OTP_RELEASE).
f([], _) -> empty;
f([_|_], _) -> cons;
f([] ++ _, OtpRelease) when OtpRelease >= 23 -> empty_append;
f(_, _) -> error(nah).
EDIT:
In the last example, OTP23's code optimizer is able to remove unreachable code. In this particular case, if f/2 is not exported, its 3rd or 4th clauses will be removed depending on ?OTP_RELEASE)
Related
I'm learning Reasonml, and I can't find any function in the standard library to do so, neither of the Bucklescript Js modules. Is there any better option than using raw javascript?
Right now I'm achieving it with this function:
let parseint: string => int = [%raw {| x => parseInt(x, 10) |}];
int_of_string (and also float_of_string / bool_of_string) should do what you need.
It's in the standard lib, you should be able to search for it https://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html (that site will work better for you if you have the reason-tools browser extension installed so it auto-converts from OCaml to Reason syntax for you)
Note that all of those functions will throw an exception if the string isn't valid for that type (read the link to see how each works and what each expects for the string).
As #glennsl points out, when Bucklescript catches up with a more recent version of the OCaml compiler than 4.02.3, you may want to use the safer _opt variants, e.g. int_of_string_opt that returns a Some(number) or None instead, depending on how much you trust the input, how much you want to avoid exceptions, and how you want to deal with bad input (is it exceptional and should kill the program/stack, or is it normal and should be handled locally?).
In functional optics, a well-behaved prism (called a partial lens in scala, I believe) is supposed to have a set function of type 'subpart -> 'parent -> 'parent, where if the prism "succeeds" and is structurally compatible with the 'parent argument given, then it returns the 'parent given with the appropriate subpart modified to have the 'subpart value given. If the prism "fails" and is structurally incompatible with the 'parent argument, then it returns the 'parent given unmodified.
I'm wondering why the prism doesn't return a 'parent option (Maybe for Haskellers) to represent the pass/fail nature of the set function? Shouldn't the programmer be able to tell from the return type whether the set was "successful" or not?
I know there's been a lot of research and thought put into the realm of functional optics, so I'm sure there must be a definitive answer that I just can't seem to find.
(I'm from an F# background, so I apologize if the syntax I've used is a bit opaque for Haskell or Scala programmers).
I doubt there's one definitive answer, so I'll give you two here.
Origin
I believe prisms were first imagined (by Dan Doel, if my vague recollection is correct) as "co-lenses". Whereas a lens from s to a offers
get :: s -> a
set :: (s, a) -> s
a prism from s to a offers
coget :: a -> s
coset :: s -> Either s a
All the arrows are reversed, and the product, (,), is replaced by a coproduct, Either. So a prism in the category of types and functions is a lens in the dual category.
For simple prisms, that s -> Either s a seems a bit weird. Why would you want the original value back? But the lens package also offers type-changing optics. So we end up with
get :: s -> a
set :: (s, b) -> t
coget :: a -> s
coset :: t -> Either s b
Suddenly what we're getting back in the non-matching case may actually be a bit different! What's that about? Here's an example:
cogetLeft :: a -> Either a x
cogetLeft = Left
cosetLeft :: Either b x -> Either (Either a x) b
cosetLeft (Left b) = Right b
cosetLeft (Right x) = Left (Right x)
In the second (non-matching) case, the value we get back is the same, but its type has been changed.
Nice hierarchy
For both Van Laarhoven (as in lens) and profunctor style frameworks, both lenses and prisms can also stand in for traversals. To do that, they need to have similar forms, and this design accomplishes that. leftaroundabout's answer gives more detail on this aspect.
To answer the “why” – lenses etc. are pretty rigidly derived from category theory, so this is actually quite clear-cut – the behaviour you describe just drops out of the maths, it's not something anybody defined for any purpose but follows from far more general ideas.
Ok, that's not really satisfying.
Not sure if other languages' type systems are powerful enough to express this, but in principle and in Haskell, a prism is a special case of a traversal.
A traversal is a way to “visit” all occurences of “elements” within some “container”. The classical example is
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
This is typically used like
Prelude> mapM print [1..4]
1
2
3
4
[(),(),(),()]
The focus here is on: sequencing the actions/side-effects, and gathering back the result in a container with the same structure as the one we started with.
What's special about a prism is simply that the containers are restricted to contain either one or zero elements† (whereas a general traversal can go over any number of elements). But the set operator doesn't know about that because it's strictly more general. The nice thing is that you can therefore use this on a lens, or a prism, or on mapM, and always get a sensible behaviour. But it's not the behaviour of “insert exactly once into the structure or else tell me if it failed”.
Not that this isn't a sensible operation, just it's not what lens libraries call “setting”. You can do it by explicitly matching and re-building:
set₁ :: Prism s a -> a -> s -> Maybe s
set₁ p x = case matching p x of
Left _ -> Nothing
Right a -> Just $ a ^. re p
†More precisely: a prism seperates the cases: a container may either contain one element, and nothing else apart from that, or it may have no element but possibly something unrelated.
I normally use the for in loop in swift without parentheses, but today I put them on just for kicks thinking they were just optional and it did not worked as expected.
This code works:
if let tasks = getAllTasksWithManagedObjectContext(appDelegate.managedObjectContext){
for task in tasks{
appDelegate.managedObjectContext.deleteObject(task)
}
}
This one does not:
if let tasks = getAllTasksWithManagedObjectContext(appDelegate.managedObjectContext){
for (task in tasks){
appDelegate.managedObjectContext.deleteObject(task)
}
}
I get this errors:
Whats going on here?
You are simply not allowed to use parentheses here.
Take a look at the Language Reference -> Statements and compare the C-style for-loop against the swift for-in
for-statement → for for-init;expression;expression code-block
for-statement → for (for-init;expression;expression) code-block
vs.
for-in-statement → for case(opt) pattern in expression where-clause(opt) code-block
The first one can be used with or without parentheses - your choice as the developer.
However the later one, the one you are actually asking about does not have a version with ( and ), only the one version without them. That means that it is not allowed to use them parentheses around the "argument" of the loop.
Screenshots from the docs linked above for better readability:
vs.
Let's say I have a pair of conversion functions
string2int :: String -> Maybe Int
int2string :: Int -> String
I could represent these fairly easily using Optics.
stringIntPrism :: Prism String Int
However if I want to represent failure reason, I'd need to keep these as two separate functions.
string2int :: String -> Validation [ParseError] Int
int2string :: Int -> String`
For this simple example Maybe is perfectly fine, since we can always assume that a failure is a parse failure, thus we don't actually have to encode this using an Either or Validation type.
However imagine, in addition to my parsing Prism, I want to perform some validation
isOver18 :: Int -> Validation [AgeError] Int
isUnder55 :: Int -> Validation [AgeError] Int
It would be ideal to be able compose these things together, such that I could have
ageField = isUnder55 . isOver18 . string2Int :: ValidationPrism [e] String Int
This is fairly trivial to build by hand, however it seems like a common enough concept that there might be something lurking in the field of Lenses/Optics that does this already. Is there an existing abstraction that handles this?
tl;dr
Is there a standard way of implementing a partial lens / prism / iso that can be parameterised over an arbitrary functor instead of being tied directly to Maybe?.
I've used Haskell notation above since it's more straight forward, however I'm actually using Monocle in Scala to implement this. I would, however, be perfectly happy with an answer specific to i.e. ekmett's Lens library.
I have recently written a blog post about indexed optics; which explores a bit how we can do coindexed optics as well.
In short: Coindexed-optics are possible, but we have yet to do some further research there. Especially, because if we try to translate that approach into lens encoding of lenses (from Profunctor to VL) it gets even more hairy (but I think we can get away with only 7 type-variables).
And we cannot really do this without altering how indexed optics are currently encoded in lens. So for now, you'll better to use validation specific libraries.
To give a hint of the difficulties: When we try to compose with Traversals, should we have
-- like `over` but also return an errors for elements not matched
validatedOver :: CoindexedOptic' s a -> (a -> a) -> s -> (ValidationErrors, s)
or something else? If we could only compose Coindexed Prisms their value won't justify their complexity; they won't "fit" into optics framework.
Why using foreach, map, flatMap etc. are considered better than using get for Scala Options? If I useisEmpty I can call get safely.
Well, it kind of comes back to "tell, don't ask". Consider these two lines:
if (opt.isDefined) println(opt.get)
// versus
opt foreach println
In the first case, you are looking inside opt and then reacting depending on what you see. In the second case you are just telling opt what you want done, and let it deal with it.
The first case knows too much about Option, replicates logic internal to it, is fragile and prone to errors (it can result in run-time errors, instead of compile-time errors, if written incorrectly).
Add to that, it is not composable. If you have three options, a single for comprehension takes care of them:
for {
op1 <- opt1
op2 <- opt2
op3 <- opt3
} println(op1+op2+op3)
With if, things start to get messy fast.
One nice reason to use foreach is parsing something with nested options. If you have something like
val nestedOption = Some(Some(Some(1)))
for {
opt1 <- nestedOption
opt2 <- opt1
opt3 <- opt2
} println(opt3)
The console prints 1. If you extend this to a case where you have a class that optionally stores a reference to something, which in turn stores another reference, for comprehensions allow you to avoid a giant "pyramid" of None/Some checking.
There are already excellent answers to the actual question, but for more Option-foo you should definitely check out Tony Morris' Option Cheat Sheet.
The reason it's more useful to apply things like map, foreach, and flatMap directly to the Option instead of using get and then performing the function is that it works on either Some or None and you don't have to do special checks to make sure the value is there.
val x: Option[Int] = foo()
val y = x.map(_+1) // works fine for None
val z = x.get + 1 // doesn't work if x is None
The result for y here is an Option[Int], which is desirable since if x is optional, then y might be undetermined as well. Since get doesn't work on None, you'd have to do a bunch of extra work to make sure you didn't get any errors; extra work that is done for you by map.
Put simply:
If you need to do something (a procedure when you don't need to capture the return value of each invocation) only if the option is defined (i.e. is a Some): use foreach (if you care about the results of each invocation, use map)
If you need to do something if the option defined and something else if it's not: use isDefined in an if statement
If you need the value if the option is a Some, or a default value if it is a None: use getOrElse
Trying to perform our Operations with get is more imperative style where u need to tel what to do and how to do . In other words , we are dictating things and digging more into the Options internals. Where as map,flatmap are more functional way of doing things where we are say what to do but not how to do.