I have a simple function like this:
def currencyConverter({ from, to, amount }) when is_float(amount) do
result = exchangeConversion({ from, to, amount })
exchangeResult = resultParser(result)
exchangeResult
end
I want to guarantee that from and to are strings and amount are float, and if not, display send a customize error message instead erlang error
What's the best way to do this?
you can make two functions with same name and arity, one with guards and one without
def currencyConverter({from, to, amount}) when is_float(amount) and is_bitstring(to) and is_bitstring(from) do
result = exchangeConversion({ from, to, amount })
exchangeResult = resultParser(result)
exchangeResult
end
def currencyConverter(_), do: raise "Custom error msg"
If you want to check the input type, you will need to create a function to do it because elixir does not have a global one.
def currencyConverter({from, to, amount}) when is_float(amount) and is_bitstring(to) and is_binary(from) do
result = exchangeConversion({ from, to, amount })
exchangeResult = resultParser(result)
exchangeResult
end
def currencyConverter({from, to, amount}) do
raise """
You called currencyConverter/1 with the following invalid variable types:
'from' is type #{typeof(from)}, need to be bitstring
'to' is type #{typeof(to)}, need to be bitstring
'amount' is type #{typeof(amount)}, need to be float
"""
end
def typeof(self) do
cond do
is_float(self) -> "float"
is_number(self) -> "number"
is_atom(self) -> "atom"
is_boolean(self) -> "boolean"
is_bitstring(self)-> "bitstring"
is_binary(self) -> "binary"
is_function(self) -> "function"
is_list(self) -> "list"
is_tuple(self) -> "tuple"
true -> "ni l'un ni l'autre"
end
end
(typeof/1 function is based on this post:https://stackoverflow.com/a/40777498/10998856)
Related
My challenge is that in the database, JSON code was untidily stored.
{'isr_comment':'Test Comment',
'isr_depression_1': '1',
'isr_depression_2': '1'
'isr_depression_3': '1'
'isr_tested': 'true'
}
You see, all values are defined as string but some should be integers. It would be the best to have clean data already in the database but I cannot control how the data is entered. However my model looks like this.
class SessionPart < ApplicationRecord
...
serialize :answers, JSON
...
end
As expected after deserialization is done I get strings as well.
#data=
{"isr_Comment"=>"Test Comment",
"isr_depression_1"=>"1",
"isr_depression_2"=>"1",
"isr_depression_3"=>"1",
"isr_tested" => "true"}
But I need to do some calculation with this data so I need all possible values with a meaningful type.
#data=
{"isr_Comment"=>"Test Comment",
"isr_depression_1"=>1,
"isr_depression_2"=>1,
"isr_depression_3"=>1,
"isr_tested" => true}
Is there any way to cast such data automatically?
You can pass your custom serializer to serialize function. That custom serializer would use JSON as source serializer and update the values as per your requirements.
class SessionPart < ApplicationRecord
...
serialize :answers, CustomSerializer #CustomSerializer must write 2 class level function named dump & load for serializing and de-serializing respectively
...
end
class CustomSerializer
def self.load(value)
normalize_hash(JSON.load(value))
end
def self.dump(value)
JSON.dump(value)
end
private
def self.normalize_hash hash
return hash unless hash.is_a? Hash
hash.transform_values {|v| normalize(v)}
end
#change this function as per your requirement, Currently it's handling boolean,integer,float & null rule set
def self.normalize(value)
case (value)
when 'true'
true
when 'false'
false
when 'null','nil'
nil
when /\A-?\d+\z/
value.to_i
when /\A-?\d+\.\d+\z/
value.to_f
else
value.is_a?(Hash) ? normalize_hash(value) : value
end
end
end
The suggested CustomSerializer seems to do its job very well, thanks. I did some small adjustments to be able to nest hashes.
class CustomSerializer
def self.load(value)
normalize_hash(JSON.load(value))
end
def self.dump(value)
JSON.dump(value)
end
private
def self.normalize_hash hash
return hash unless hash.is_a? Hash
hash.transform_values {|v| normalize(v) }
end
#change this function as per your requirement, Currently it's handling boolean,integer,float & null rule set
def self.normalize(value)
case (value)
when 'true'
true
when 'false'
false
when 'null','nil'
nil
when /\A-?\d+\z/
value.to_i
when /\A-?\d+\.\d+\z/
value.to_f
else
value.is_a?(Hash) ? normalize_hash(value) : value
end
end
end
A standard construct in my code is a function that returns a Reader[X,\/[A,B]] and I would like to use the Either portion in a for comprehension, so I have been trying to write a function which will convert a function (X) => \/[A,B] into EitherT[Reader[X,\/[A,B]],A,B].
I can do this with a predetermined Type for X. For instance:
case class Config(host: String)
type ReaderConfig[C] = Reader[Config, C]
type EitherReaderConfig[A,B] = EitherT[ReaderConfig, A,B]
def eitherReaderF[A,B](f: Config => \/[A,B]) : EitherReaderConfig[A,B] = EitherT[ReaderConfig, A,B](Reader[Config, \/[A,B]](f))
eitherReaderF(c => \/-(c.host)).run(Config("hostname"))
However, I am having problems removing the Config type and generalizing over X. This is because EitherT's first argument is expecting one argument in it's type construct: F[_], however Reader is defined as containing 2: Reader[A,B]
One of my attempts is to define a type in terms of an EitherT using type lambdas.
type EitherReaderM[X,A,B] = EitherT[({type λ[α] = Reader[X, α]})#λ, A,B]
def eitherReaderM[X,A,B](f: X => \/[A,B]): EitherReaderM[X,A,B] = EitherT[({type λ[α] = Reader[X, α]})#λ, A,B](Reader(f))
val r: EitherReaderM[Config, Int, String] = eitherReaderM((c: Config) => \/-(c.host))
val run = r.run /// type returns scalaz.Kleisli[[+X]X,Config,scalaz.\/[Int,String]]
run.apply(Config("host")) // fails: value apply is not a member of scalaz.Kleisli[[+X]X,Config,scalaz.\/[Int,String]]
That last bit fails. I feel like I'm close here.....
I'm not entirely sure what's going on yet, but I can run this with 2 calls to run. One on the EitherT and then one on the Kleisli (which I'm not sure where it came in ).
run.run(Config("host"))
However, even though this runs in the console, it doesn't actually compile. I receive this error while compiling:
kinds of the type arguments ([α]scalaz.Kleisli[[+X]X,X,α],A,B)
do not conform to the expected kinds of the type parameters (type F,type A,type B).
[α]scalaz.Kleisli[[+X]X,X,α]'s type parameters do not match type F's expected parameters:
type α is invariant, but type _ is declared covariant
[ERROR] def eitherReaderM[X,A,B](f: X => /[A,B]): EitherReaderM[X,A,B] = EitherT({type λ[α] = Reader[X, α]})#λ, A,B
And here we have it, the final, compiling version. I feel like it can be simplified a little more, but that is for a different day.
type EitherReader[X,A,B] = EitherT[({type λ[+α] = Reader[X, α]})#λ, A,B]
def eitherReader[X,A,B](f: X => \/[A,B]): EitherReader[X,A,B] = EitherT[({type λ[+α] = Reader[X, α]})#λ, A,B](Reader(f))
The allows me to replace Reader[X, A / B].apply with eitherReader[X,A,B].
Old:
def getSub(id: Int) = Reader[Config, String \/ Sub](config => config.findSub(id).right)
New:
def getSub(id: Int) = eitherReader[Config, String, Sub]](config => config.findSub(id).right)
Seems weird that I'm doing this simply for type conversion. Probably means I'm overlooking something.
I'm struggling a bit with this: I need a function that takes any function
of type fun(Any*) : Boolean as parameter, evaluates the function and returns true or
false, depending on the success of the function evaluation.
Essentially, what I need is a function type that allows any number and any type of parameter but the function must return Boolean.
Which would allow me to write functions like:
def checkLenght(str : String, length : Int) : Boolean ={
if (str.lenght == length)}
or
def ceckAB(a : Int, b : Int) : Boolean = {
if(a < b && a >= 23 && b < 42) }
so that, for example
eval(checkLenght(abc, 3)) //returns true
eval(ceckAB(4,1)) // returns false
I thought, a function type of:
type CheckFunction = (Any*) => Boolean
may does the trick but I struggle with writing the generic eval function.
Any advise?
Thank you
Solution:
The function requires
1) Another function of return type Boolean: "(func : => Boolean)"
2) Return type Boolean ": Boolean"
3) Returns the value of the passed function-parameter: " = func"
Altogether the function is:
def eval(func : => Boolean) : Boolean = func
It amazes me over again how simple simple things are in Scala.
As pointed out by the comments, this is a rather unusual function with no obvious
sense. Just a word about the underlying reasons.
Motivation:
There were a lot of question about the underlying motivation, so here a short
summary why such a function is needed.
Essentially, there are two reasons.
First one is about moving the failure handling away from the function itself
into a handler function. This preserves the purity of the check function and even allows
re-usage of generic checks.
Second, it's all about "pluggable failure handling". This means, the eval function only
tells if a failure happened (or not). In case of a failure, a handler is called through an interface. The implementation of the handler can be swapped using profiles as required.
Why?
Swapping profiles means, I code my checks and functions as usual but by switching the
profile, I switch the handler which means I can chose between full-stop, console print out, email alert, SNMP notification, push message... you name it. To do so, I need to decouple the check function from its evaluation and from its handling. That's the motivation for such a rather strange looking eval function.
And for the sake of completeness, I've already implemented all that stuff but was I facing the limitation of only handling trivial checks i.e. check(Boolean*) which is neat but often I would prefer to write a function to do more sophisticated checks.
Solved
The function is defined by returning the value of the passed function:
def eval(func : => Boolean) : Boolean = {func}
I can't say that I really understand your motivations for wanting to do what you want to do, but I guess that's beside the point. Maybe the eval function will check something before invoking the supplied function and not invoke that other function (like a fast fail) given some certain condition. Maybe you do some post checking after invoking the function and change the result based on something else. Either way, I suppose you could accomplish something similar to what you want with code looking like this:
def main(args: Array[String]) {
val str = "hello world"
println(eval(checkLength(str, 3)))
println(eval(intsEqual(1,1)))
}
def eval(func: => Boolean):Boolean = {
//Do whetever you want before invoking func, maybe
//not even invoke it if some other condition is present
val fres = func
//Maybe change something here before returning based on post conditions
fres
}
def checkLength(s:String, len:Int) = s.length() == len
def intsEqual(a:Int, b:Int) = a == b
If you really want the eval function to be able to support any function that takes any types of args and returns a Boolean, then using a by-name function like this, and then leveraging closure inside the by-name function to pass any params along to whatever actual function you want to invoke. A better way to demonstrate this is as follows:
def checkMyString(str:String, len:Int) = {
eval(str.length == len)
}
It's probably hard to see that the check str.length == len is not invoked unless eval decides to invoke it until you expand it to it's true form:
def checkMyString(str:String, len:Int) = {
def check = {
str.length == len
}
eval(check)
}
Here, the nested function check has access to str and len due to closure, and this will allow you to get around the requirement that eval must be able to invoke a function with any params that returns a Boolean.
This is just one way to solve your problem, and it might not even be suitable given your needs, but I just wanted to throw it out there.
If your input functions only have 2 arguments, like your two examples, you can write a semi generic function take takes all functions with two arguments of any type:
def eval[A,B](func: (A,B) => Boolean, arg1: A, arg2: B) = {
func(arg1, arg2)
}
def checkLength(str: String, length: Int) : Boolean = {
str.length == length
}
eval(checkLength, "ham", 4)
res0: Boolean = false
But if you want to support functions with more arguments, you would have to write one eval function for three arguments, four arguments, etc
Maybe there is a better way that can handle all cases?
I am trying to figure out the issue, and tried different styles that I have read on Scala, but none of them work. My code is:
....
val str = "(and x y)";
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int )
var b = pos; //position of where in the expression String I am currently in
val temp = expreshHolder; //holder of expressions without parens
var arrayCounter = follow; //just counts to make sure an empty spot in the array is there to put in the strings
if(exp(b) == '(') {
b = b + 1;
while(exp(b) == ' '){b = b + 1} //point of this is to just skip any spaces between paren and start of expression type
if(exp(b) == 'a') {
temp(arrayCounter) = exp(b).toString;
b = b+1;
temp(arrayCounter)+exp(b).toString; b = b+1;
temp(arrayCounter) + exp(b).toString; arrayCounter+=1}
temp;
}
}
val hold: ArrayBuffer[String] = stringParse(str, 0, new ArrayBuffer[String], 0);
for(test <- hold) println(test);
My error is:
Driver.scala:35: error: type mismatch;
found : Unit
required: scala.collection.mutable.ArrayBuffer[String]
ho = stringParse(str, 0, ho, 0);
^one error found
When I add an equals sign after the arguments in the method declaration, like so:
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int ) ={....}
It changes it to "Any". I am confused on how this works. Any ideas? Much appreciated.
Here's a more general answer on how one may approach such problems:
It happens sometimes that you write a function and in your head assume it returns type X, but somewhere down the road the compiler disagrees. This almost always happens when the function has just been written, so while the compiler doesn't give you the actual source (it points to the line where your function is called instead) you normally know that your function's return type is the problem.
If you do not see the type problem straight away, there is the simple trick to explicitly type your function. For example, if you thought your function should have returned Int, but somehow the compiler says it found a Unit, it helps to add : Int to your function. This way, you help the compiler to help you, as it will spot the exact place, where a path in your function returns a non-Int value, which is the actual problem you were looking for in the first place.
You have to add the equals sign if you want to return a value. Now, the reason that your function's return value is Any is that you have 2 control paths, each returning a value of a different type - 1 is when the if's condition is met (and the return value will be temp) and the other is when if's condition isn't (and the return value will be b=b+1, or b after it's incremented).
class Test(condition: Boolean) {
def mixed = condition match {
case true => "Hi"
case false => 100
}
def same = condition match {
case true => List(1,2,3)
case false => List(4,5,6)
}
case class Foo(x: Int)
case class Bar(x: Int)
def parent = condition match {
case true => Foo(1)
case false => Bar(1)
}
}
val test = new Test(true)
test.mixed // type: Any
test.same // type List[Int]
test.parent // type is Product, the case class super type
The compiler will do its best to apply the most specific type it can based on the possible set of result types returned from the conditional (match, if/else, fold, etc.).
I am not sure if it's even possible.
Say I have the following function defined:
a(), b(), c()
I want to get a user input from command line (either "a", "b", "c") and according to the input call the corresponding function
BUT without using cases or if's
ie:
input = getinput()
if exist?(input){
input()
}
Does this exist in any language?
Sure you can do something like that. For instance, in Python:
def a():
return 'a'
def b():
return 'b'
def select_func(name):
return eval(name)()
Executing select_func('a') will return 'a', and executing select_func('c') will raise an exception