Can Crystal-lang method return multiple values with specific type?
I know that it can be implemented in this way:
def my_method(arg_1 : Int, arg_2 : String) : Tuple
return arg_1, arg_2
end
res_1, res_2 = my_method(1, "1")
but it also work if I do:
result = my_method(1, "1") #=> {1,"2"}
but can I do somethink like in Go-lang
def my_method(arg_1 : Int, arg_2 : String) : Int, String
return arg_1, arg_2
end
???
Thanks!
Crystal methods can only return one value. The way to "return multiple values" is by returning a tuple and then, if you want, immediately unpack it at the call site, like what you did.
If you want to specify the return type you can do:
def my_method(arg_1 : Int, arg_2 : String) : {Int32, String}
return arg_1, arg_2
end
Or (the same, just another syntax):
def my_method(arg_1 : Int, arg_2 : String) : Tuple(Int32, String)
return arg_1, arg_2
end
You can also use a shorter syntax to return multiple values:
def my_method(arg_1 : Int, arg_2 : String)
{arg_1, arg_2}
end
That is, doing return 1, 2 is internally the same as returning the tuple {1, 2}.
In the end, it doesn't really matter how this is implemented, maybe in Go the function doesn't really return two values but passes pointers or something like that, and then in assembly there aren't even functions, so what matters if you can return multiple things and then get them all at once, somehow.
Related
I'm learning Scala and in a book that I'm reading (Functional Programming in Scala) I came across an example of a custom List implementation in Scala which goes like this:
sealed trait MyList[+A]
case object MyNil extends MyList[Nothing]
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil
else Cons(as.head, apply(as. tail: _*))
}
I would like to extend MyList to add the following functionality:
add a tail method that returns all elements of a MyList instance without the first one, e.g. val x = MyList(1,2,3); x.tail == MyList(2,3).
Add a sum method that is only applicable when MyList contains Ints (or even better for all numeric types). So e.g. val x = MyList(1,2,3); x.sum == 6
The idea above 2 questions is to understand: (1) how to interact with the instance of my class and (2) how to use polymorphism in a situation like this. After some searching around, I'm not even sure how to begin with these problems, which is why I'm asking this question.
Any tips would be appreciated. Many thanks!
UPDATE:
A few updates:
First, I'd like to point out that the solution to the programming challenges in the Functional Programming course that I mentioned earlier can be found here, however, I'm looking for something a little different than what the author is asking for.
I've managed to find an answer to my first question "how can I use tail on my instance itself, e.g. MyList(1,2,3).tail?". To solve this, I had to modify the original trait in the following manner:
sealed trait MyList[+A] {
def tail: MyList[A] = MyList.tail(this)
}
I'm not sure if this is the best way of doing what I want to do, but it works. If anyone has better suggestions, please let me know.
The second part is harder. I wanted to add the following inside the same trait:
def sum[Int]: MyList[Int] = MyList.sum(this)
But IntelliJ is complaining about the type of this which is A and I need to apply this conditionally on this being of type Int.
Another alternative is to do the following:
def sum: Int = this match {
case x: MyList[Int] => MyList.sum(x)
}
But what if we want to create another implementation for String that will also return a String? This cannot be the right solution and I haven't found one yet. Please help :)
.tail
I note that your Cons class already has a public tail member. I'd be tempted to start there and just make it universal...
sealed trait MyList[+A] {
def tail: MyList[A]
}
...and add the MyNil implementation.
case object MyNil extends MyList[Nothing] {
def tail: MyList[Nothing] =
throw new java.lang.UnsupportedOperationException("tail of empty list")
}
This is how the standard library List handles the tail of an empty list. Another, perhaps gentler, option would be to return this so that the tail of an empty MyList is just the empty MyList.
Leaving class Cons and object MyList unchanged, we get the expected results.
MyList('s','h','o','w').tail //res0: MyList[Char] = Cons(h,Cons(o,Cons(w,MyNil)))
MyList(9).tail.tail //java.lang.Unsupported...
.sum
This is a bit trickier. We want each .sum invocation to compile only if the elements are of a sum-able type, such as Int. The Scala way to achieve this to require that the call site provide implicit "evidence" that the element type is acceptable.
sealed trait MyList[+A] {
def sum(implicit ev : A =:= Int) : Int //can sum only if A is Int
}
Alas, this won't compile because MyList is covariant on A, but being the type of a passed parameter puts A in a contra-variant position.
Error: covariant type A occurs in invariant position in type A =:= Int of value ev
Fortunately there's a fix for that: use a different type parameter, related to A but not restricted to its covariant relationship.
sealed trait MyList[+A] {
def sum[B >: A](implicit ev : B =:= Int) : Int = 0 //default behavior
}
case object MyNil extends MyList[Nothing] { ... //unchanged
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev :B =:= Int) : Int = head + tail.sum[B]
}
object MyList { ... //unchanged
MyList(23,31,12).sum //res0: Int = 66
MyList("as","is").sum //won't compile
Numeric[A]
Well that works for Int, but it would be a pain to have to do the same for every sum-able type. Fortunately the standard library offers the Numeric typeclass which provides some basic values (zero and one) and operations (plus(), minus(), times(), etc.) for all the numeric types under its umbrella (Short, Long, Float, etc.).
So, putting it all together:
sealed trait MyList[+A] {
val tail: MyList[A]
def sum[B >: A](implicit ev : Numeric[B]): B = ev.zero
}
case object MyNil extends MyList[Nothing] {
val tail: MyList[Nothing] = this
}
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev : Numeric[B]): B = ev.plus(head, tail.sum[B])
}
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil else Cons(as.head, apply(as.tail: _*))
}
I have a c library that I bind to, which has many structs that hold data of different types.
#[Link("foo")]
lib LibFoo
struct IntStruct
data : LibC::Int
end
struct DoubleStruct
data : LibC::Double
end
fun get_int_struct(n : LibC::SizeT) : IntStruct*
fun get_double_struct(n : LibC::SizeT) : DoubleStruct*
end
I have a generic class where I want the type T to be the type of one of those structs, based off of an input array.
class GenericClass(T)
#ptr : Pointer(T)
#obj : T
def initialize(arr : Array)
#ptr = infer_type(arr)
#obj = #ptr.value
end
end
Where infer_type looks like:
def infer_type(data : Array(Int32))
return LibFoo.get_int_struct(data.size)
end
def infer_type(data : Array(Float64))
return LibFoo.get_double_struct(data.size)
end
The code works fine when I explicitly set the type...
GenericClass(LibFoo::IntStruct).new [1, 2, 3, 4, 5]
...but I would like to infer the type.
Maybe providing a custom generalized new method works for you?
struct A
end
struct B
end
class Generic(T)
def self.new(items : Array(A)) forall A
new(fetch_struct(items))
end
private def initialize(#struct : T)
end
private def self.fetch_struct(items : Array(Int32))
A.new
end
private def self.fetch_struct(items : Array(Float64))
B.new
end
end
p Generic.new([1])
p Generic.new([1.0])
https://carc.in/#/r/7s6d
So my short code snippet looks like the following:
Source.fromFile(fileName).getLines().foldLeft(List[CsvEntry]())((csvList, currentLine) =>
currentLine match {
case pattern(organisation,yearAndQuartal,medKF,trueOrFalse,name,money) => new CsvEntry(organisation,yearAndQuartal,medKF.toInt,trueOrFalse.toInt,name,money) :: csvList
case default => csvList
The "fileName" is only the Name of the file, but it doesn't matter for my question.
And my csvList is defined like this:
type csvList = List[CsvEntry]
val list: csvList = List()
my class look like this:
class CsvEntry(val organisation: String, val yearAndQuartal : String, val medKF:Int, val trueOrFalse: Int, val name: String, val money:String){
override def toString = s"$organisation, $yearAndQuartal, $medKF,$trueOrFalse, $name, $money"
So my question is, whenever I am loading a file and writing it to my csvList it works, but when I am loading another 2nd file the old content gets overwirtten.
How can I change it in order to not get overwirtten, so it should only add it to the preceding data ?
The call beginning Source.fromFile... returns a list that you should then combine with the next call.
For example:
List("filename1", "filename2").map(processFile).flatten
where processFile is:
def processFile(fileName: String) = {
Source.fromFile(fileName).getLines().foldLeft... all the code in question
}
Nothing here can possibly get "overwritten", since there's no mutable state. Your type csvList and csvList in your foldLeft call are two very different things: the former is the type, the latter is the parameter.
Each time your snippet is executed, it returns a list of your CSV objects of a particular file. As an immutable list.
What you want is:
Make a function out of your snippet: def readFile(name: String): List[CsvFile]
Call it on the two files and save results to vals
Concat the two lists: list1 ++ list2
I am using some functions that return Options, but I would like to replace them by PartialFunctions in order to use the collect function in Scala. In detail I am trying to call collect (and collectFirst) on a list of objects that contain a partial function in the following way:
class A(val bs : List[B]) {
def getAll(i : Int): List[Int] = bs.map(_.foo(i))
}
class B(val y : Int) {
val foo : PartialFunction[Int, Int] = {
case x if x > y => y
}
}
The above code compiles and does what I want to if the function foo is defined for all values in bs.
val a = new A(List(new B(1), new B(2), new B(3)));
println(a.getAll(5))
prints "List(1, 2, 3)", but of course I run into an error if foo is not defined for a value. Hence, I want to replace it by "collect"
def getAll(i : Int): List[Int] = bs.collect(_.foo(i))
In my understanding collect should work pretty much the same as map, yet still the above code does not compile (I get a type mismatch because foo cannot be resolved). What is the best way in this case?
collect expects to receive a partial function (not a function or lambda), that will take an element of your collection as an input: PartialFunction[B, Int]. But here you want just to call your PartialFunction[Int, Int] inside another function, so you don't even passing a PartialFunction into collect (you're passing function (_.foo(i)): B => Int).
Solution without collect (but still with PartialFunction as a member of B):
def getAll(i : Int): List[Int] = bs.flatMap(_.foo.lift(i))
lift rises your function from PartialFunction[Int, Int] to Int => Option[Int] here.
If you really really want to collect:
import Function._
def getAll(i : Int): List[Int] = bs.collect(unlift(_.foo.lift(i)))
unlift reduces B => Option[Int] into PartialFunction[B, Int]
The ugliest version is collect{ case x if x.foo.isDefinedAt(i) => x.foo(i) }
Better solution is to move your partial function outside of B:
case class A(bs : List[B]) {
def getAll(x : Int): List[Int] = bs.collect {
case B(y) if x > y => y
}
}
case class B(val y : Int)
After a lot of Java and some Haskell I wanted to have a look at Scala. From the code below, I'm getting this error message
type mismatch; found : List[Nothing] => Option[Nothing] required: List[Int] => Option[Nothing]
I don't know what I'm doing wrong:
object MyFirstScalaObject {
def main(args: Array[String]) {
lazy val testValues:List[List[Int]] = List((1 to 10).toList, null, List());
println( testFunction(last, testValues));
}
def testFunction[I, O](f : I => O, inputs : List[I]):
List[(I, O)] =
inputs.zip(inputs.map(f));
def last[A](xs:List[A]):Option[A] = xs match {
case x::Nil => Some(x);
case _::xs => last(xs);
case _ => None;
}
}
Thanks for any advice.
Cheers,
because of the way type inference works in scala, it is unable to determine the what the type parameter to last has to be, so it has to take the overly conservative fallback guess that it is Nothing.
You can explicitly specify the types when you call testFunction:
testFunction[List[Int],Option[Int](last, testValues)
or you can document more fully the relationship between the type parameters in the testFunction declaration, which will give the type inferencer more information:
def testFunction[A, I[_], O[_]](f : I[A] => O[A], inputs : List[I[A]]): List[(I[A], O[A])]
This explicitly says that I and O are type constructors (kind * -> *), now that the input/output types of f are more specific, the inferencer can correctly infer that the A parameter to the the last function must be Int.
A fully revised & tested version, using senia's idea:
object MyFirstScalaObject {
def main(args: Array[String]) {
lazy val testValues = List((1 to 10).toList, null, List())
println(testFunction(testValues)(last))
}
def testFunction[I, O](inputs: List[I])(f: I => O): List[(I, O)] =
inputs.zip(inputs.map(f))
def last[A](xs: List[A]): Option[A] = xs match {
case x :: Nil => Some(x)
case _ :: xs => last(xs)
case _ => None
}
}
Type inference proceeds left-to-right; information from one parameter list is used within next parameter list.
In this code, when you call testFunction Scala can deduce I from the first parameter, then it can feed I as the input type of the function f to figure out its type (that is, that the argument last is applied with A = Int), then it finally gets the value of O from the return type of the function.
I can't tell you why type inference in scala works this way.
But there is common way to help compiler in such cases - parameter sections.
def testFunction[I, O](inputs : List[I])(f: I => O): List[(I, O)] = inputs.zip(inputs.map(f))
Usage:
testFunction(testValues)(last)
Similar solution is to add method testFunction to class List:
class LastsHelper[T](inputs: List[T]) {
def testFunction[O](f: T => O): List[(T, O)] = inputs.zip(inputs.map(f))
}
implicit def toLastsHelper[T](inputs: List[T]) = new LastsHelper(inputs)
You can use such methods like methods of List:
testValues.testFunction(last)