Convert HList to Map with shapless - shapeless

i start work with shapeless, and as proof of concept, i defined function for convert Hlist to Map, but something went wrong, can you help me please?
object bar extends Poly1 {
implicit def caseTuple[T, U](implicit st: Case.Aux[T, Map[String, Any]], su: Case.Aux[U, Map[String, Any]]) =
at[(T, U)](t => {
Map(s"${t._1}" -> t._2)
})
}
object foo extends Poly2 {
implicit def default[T](implicit st: bar.Case.Aux[T, Map[String, Any]]) =
at[Map[String, Any], T] { (acc, t) =>
acc ++ bar(t)
}
}
val h = ("k1", 1) :: ("k2", "foo") :: HNil
println(h.foldLeft(Map.empty[String, Any])(foo))
But i got error: could not find implicit value for parameter folder: shapeless.ops.hlist.LeftFolder[shapeless.::[(String, Int),shapeless.::[(String, String),shapeless.HNil]],scala.collection.immutable.Map[String,Any],Main.foo.type]
[error] println(h.foldLeft(Map.empty[String, Any])(foo))

As you don't care about the value type, you don't need dedicated polymorphic function for the value type.
If you're just relying on .toString, you don't need a Poly1 so that the above example can be rewritten like this:
object foo1 extends Poly2 {
implicit def default[K, V] =
at[Map[String, Any], (K, V)] { case (acc, (k, v)) =>
acc + (k.toString -> v)
}
}
val h = ("k1", 1) :: ("k2", "foo") :: (true, "TRUE") :: HNil
println(h.foldLeft(Map.empty[String, Any])(foo1))
// Map(k1 -> 1, k2 -> foo, true -> TRUE)
If you have multiple types in the key position, you need to convert them to string. It can be Poly1 or any typeclass such as scalaz.Show or cats.Show which provide A => String.
object bar2 extends Poly1 {
implicit val stringCase = at[String](identity)
implicit val symbolCase = at[Symbol](_.name)
implicit def integralCase[A: Integral] = at[A](_.toString)
implicit def fractionalCase[A: Fractional] = at[A](_ formatted "%.2f")
}
object foo2 extends Poly2 {
implicit def default[K, V](implicit bk: bar2.Case.Aux[K, String]) =
at[Map[String, Any], (K, V)] { case (acc, (k, v)) =>
acc + (bk(k) -> v)
}
}
val h2 = ("k1", 1) :: ("k2", "foo") :: ('k3, true) :: (12.3456, 'frac) :: HNil
println(h2.foldLeft(Map.empty[String, Any])(foo2))
// Map(k1 -> 1, k2 -> foo, k3 -> true, 12.35 -> 'frac)
You can do the same to have another key type instead of Any.

Related

Swift array map without $0 (Higher-Order Functions )

I have a function that generates a number with some conditions
func randomWithCondition() -> Int {
...
return n
}
And later I will use this function to build an array with N length in another function
func a() -> [Int] {
var arr = [Int]()
for _ in 0..<length {
arr.append(randomWithCondition())
}
return arr
}
I want to write those in one line, the best thing I can think of is
return (0..<N).map { _ in randomWithCondition() }
I have to add _ in because I didn't use $0, otherwise it won't compile.
Question: Is there a walk round to not write _ in? any other format would be fine as long as it's one line in the return statement.
map transforms each element from its type to other so that the transformation requires the parameter in the closure. But for convenience you can omit this parameter by making extensions for Range and ClosedRange:
extension Range where Bound: Strideable, Bound.Stride: SignedInteger {
public func map<T>(_ transform: () -> T) -> [T] {
map { _ in transform() }
}
}
extension ClosedRange where Bound: Strideable, Bound.Stride: SignedInteger {
public func map<T>(_ transform: () -> T) -> [T] {
map { _ in transform() }
}
}
let range = (0..<10).map { arc4random() }
print(range)
// Outputs: [676946806, 482060909, 1553829324, 1660236508, 606395000, 268649066, 1438948568, 1995527535, 918698113, 505678702]
let closedRange = (0...9).map { arc4random() }
print(closedRange)
// Outputs: [20467139, 198204705, 1585520963, 2022302907, 2518334206, 3304761403, 3782378335, 3830286797, 2200585433, 2387902936]
You need to define some extra functions yourself.
For example, a const function (name is inspired by Haskell, but you can call it withParameter instead):
func const<T, U>(_ f: #escaping () -> U) -> ((T) -> U) {
{ _ in f() }
}
Then you can do:
return (0..<N).map(const(randomWithCondition))
I think you should also wrap this in a generate function:
func generate<T>(_ count: Int, generator: #escaping () -> T) -> [T] {
(0..<count).map(const(generator))
}
Do note that you can also create lazy infinite sequences with sequence, rather than mapping a range.

Create copy of generic array with new size

Is it possible in Kotlin to create a copy of a generic array with new size if I already have an instance of that array and pass a construction method for its items? I think about something like this:
fun <T> resize(arr: Array<T>, newSize: Int, creator: (Int) -> T): Array<T> {
...
}
Obviously I cannot call Array<T>(newSize) { i -> creator(i) } because the type T is not known at compile time. For efficicy reasons I do not want to use an inline function with a reified type. I also cannot use the arr.copyOf(newSize) method here because that would return an Array<T?>.
In Java I could use Arrays.copyOf(arr, newSize) even with a generic array because we don't have null safety here. But would this work in Kotlin as well? Or do you have any other ideas what I could do?
I would just add an extension method to Array<T> for this. You can still rely on Arrays.copyOf under the hood, but before returning the value fill any remaining spaces with the result of the creator block:
fun <T> Array<T>.resize(newSize: Int, creator: (Int) -> T): Array<T> {
val copiedArray = Arrays.copyOf(this, newSize)
for (i in size until newSize) { copiedArray[i] = creator(i) }
return copiedArray
}
For example:
val array = arrayOf("a", "b", "c")
// same: [a, b, c]
println("same: ${Arrays.toString(array.resize(3) { it.toString() })}")
// less: [a, b]
println("less: ${Arrays.toString(array.resize(2) { it.toString() })}")
// more: [a, b, c, 3, 4]
println("more: ${Arrays.toString(array.resize(5) { it.toString() })}")
I guess this will work:
fun <T> resize(arr: Array<T>, dest: Array<T>, creator: (Int) -> T): Array<T> {
val list = arrayListOf<T>()
// fill the list
for (i in 0 until dest.size) {
list.add(creator(i))
}
return list.toArray(dest)
}
fun callResize() {
val arr = Array(5) { " " }
val dest = Array(10) { "" }
val creator = { i: Int -> "$i"}
resize(arr, dest, creator)
}
The result is in the dest array.

How to compare two arrays of protocols for equality in Swift?

I've run into a situation that I'm sure is not that uncommon. I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
What I'd really like to do is this:
protocol Pattern: Equatable
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs:Pattern, lhs:Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
extension Equatable where Self : Pattern
{
func isEqualTo(other: Pattern) -> Bool
{
guard let o = other as? Self else { return false }
return self == o
}
}
However, this leads to the compile error:
Error:(10, 30) protocol 'Pattern' can only be used as a generic constraint because it has Self or associated type requirements
Based on this post I realise that I need to lose the Equatable inheritance on my protocol and push it down onto the concrete 'Pattern' declarations. Though I really don't understand why. If I'm defining how the two objects are equal based on the protocol by overloading == there really is no issue as far as I can see. I don't even need to know the actual types or whether they are classes or structs.
Regardless, this is all well and good and I can now compare concretePattern.isEqualTo(otherConcretePattern) but the issue remains that I can no longer compare arrays of these objects like I can compare an array of a concrete type as array equality relies on overloading the == operator.
The best I've managed to do so far is glom an isEqualTo method onto CollectionType via an extension. This at least allows me to compare arrays. But frankly, this code stinks.
extension CollectionType where Generator.Element == Pattern
{
func isEqualTo(patterns:[Pattern]) -> Bool {
return self.count as? Int == patterns.count && !zip(self, patterns).contains { !$0.isEqualTo($1) }
}
}
Is there really no other way of doing this? Please tell me I'm missing something obvious.
I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
So you want to say the two arrays are equal if all the elements in them are equal and the elements all conform to pattern. i.e.
If a, b, c and d are all things that conform to Pattern, you want
a == c
a != b
a != d
b != d
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
array1 == array2 // true
array1 == array3 // false
The easiest way to do this is actually to define an equality operator for two arrays of patterns i.e.
protocol Pattern
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs: Pattern, lhs: Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
func ==(lhs: [Pattern], rhs: [Pattern]) -> Bool
{
guard lhs.count == rhs.count else { return false }
var i1 = lhs.generate()
var i2 = rhs.generate()
var isEqual = true
while let e1 = i1.next(), e2 = i2.next() where isEqual
{
isEqual = e1 == e2
}
return isEqual
}
I defined two types that conform to Pattern and tried various equality compares and it all works
struct Foo: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Foo else { return false }
return self.data == other.data
}
}
struct Bar: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Bar else { return false }
return self.data == other.data
}
}
let a = Foo(data: "jeremyp")
let b = Bar(data: "jeremyp")
let c = Foo(data: "jeremyp")
let d = Foo(data: "jeremy")
let comp1 = a == c // true
let comp2 = a == b // false
let comp3 = a == d // false
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
let comp4 = array1 == array2 // true
let comp5 = array1 == array3 // false
The Swift answer:
protocol _Pattern
{
func _isEqualTo(_other: Any) -> Bool?
}
extension _Pattern where Self: Pattern
{
func _isEqualTo(_other: Any) -> Bool?
{
return (_other as? Self).map({ self.isEqualTo($0) })
}
}
protocol Pattern: _Pattern, Equatable
{
func isEqualTo(other: Self) -> Bool
}
extension Pattern
{
func isEqualTo(other: _Pattern) -> Bool
{
return _isEqualTo(other) ?? false
}
}
func == <T: Pattern>(rhs: T, lhs: T) -> Bool
{
return rhs.isEqualTo(lhs)
}
This is a pattern (pun intended) that I developed myself, and it works extremely well for situations like these. _Pattern is an auto-implemented protocol courtesy of the new protocol-extension feature, and represents a type-erased version of Pattern.

Using untyped F# quotations to copy an array without knowing the type

I'm working on a small project to using quotations to clone trees of some basic record types and I have it working in most cases, the big problem I'm having is with arrays.
module FSharpType =
/// predicate for testing types to see if they are generic option types
let IsOption (stype: System.Type) = stype.Name = "FSharpOption`1"
/// predicate for testing types to see if they are generic F# lists
let IsList (stype: System.Type) = stype.Name = "FSharpList`1"
module RecordCloning =
let inline application prms expr = Expr.Application(expr, prms)
let inline coerse typ expr = Expr.Coerce(expr, typ)
let (|IsMapType|_|) (t: Type) =
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_,_>> then Some t
else None
let rec copyThing (mtype: Type) : Expr =
match mtype with
| _ when FSharpType.IsRecord mtype -> genRecordCopier mtype
| _ when FSharpType.IsUnion mtype -> genUnionCopier mtype
| _ when mtype.IsValueType || mtype = typeof<String> -> <## id ##>
| _ when mtype.IsArray -> genArrayCopier mtype
| IsMapType t -> <## id ##>
| _ when mtype = typeof<System.Object> -> <## id ##>
| _ -> failwithf "Unexpected Type: %s" (mtype.ToString())
and genRecordCopier (rtype: Type) : Expr =
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
let useArg = Expr.Coerce(argExpr, rtype)
let fields = FSharpType.GetRecordFields(rtype)
let members = [ for field in fields -> genFieldCopy useArg field ]
let newrec = Expr.Coerce(Expr.NewRecord(rtype, members),typeof<obj>)
Expr.Lambda(arg, newrec)
and genFieldCopy argExpr (field: PropertyInfo) : Expr =
let pval = Expr.PropertyGet(argExpr, field)
let convfun = copyThing field.PropertyType
let applied = Expr.Application (convfun, Expr.Coerce(pval, typeof<obj>))
Expr.Coerce(applied, field.PropertyType)
and castToType (atype : Type) : Expr =
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
Expr.Lambda(arg, Expr.Coerce(argExpr, atype))
and coerseLambda (outterType: Type) (lambda: Expr) : Expr =
let arg = Var("x", outterType, false)
let argExpr = Expr.Var(arg)
let wrappedLambda =
lambda
|> application (argExpr |> coerse typeof<obj>)
|> coerse outterType
Expr.Lambda(arg, wrappedLambda)
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
let wrappedLambda = coerseLambda etype copyfun
let func = <## Array.map (%%wrappedLambda) (%%argExpr) ##>
Expr.Lambda(arg, func)
and genOptionCopier (otype: Type) : Expr =
let etype = otype.GetGenericArguments().[0]
let copyfun = copyThing etype
<## fun (inobj: obj) ->
let x = inobj :?> Option<'t>
match x with
| Some v -> Some <| (%%copyfun) (box v)
| None -> None
|> box
##>
and genUnionCopier (utype: Type) : Expr =
let cases = FSharpType.GetUnionCases utype
// if - union case - then - copy each field into new case - else - next case
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
let useArg = Expr.Coerce(argExpr, utype)
let genCaseTest case = Expr.UnionCaseTest (useArg, case)
let makeCopyCtor (ci: UnionCaseInfo) =
let copiedMembers = [ for field in ci.GetFields() -> genFieldCopy useArg field ]
Expr.Coerce(Expr.NewUnionCase(ci, copiedMembers), typeof<obj>)
let genIf ifCase thenCase elseCase = Expr.IfThenElse(ifCase, thenCase, elseCase)
let nestedIfs =
cases
|> Array.map (fun case -> genIf (genCaseTest case) (makeCopyCtor case))
|> Array.foldBack (fun iff st -> iff st) <| <## failwith "Unexpected Case Condition" ##>
let newunion = Expr.Coerce(nestedIfs,typeof<obj>)
Expr.Lambda(arg, newunion)
let wrapInType<'I,'O> (lambdaExpr: Expr) : Expr<'I -> 'O> =
<# fun (v : 'I) -> (%%lambdaExpr : obj -> obj) (box v) :?> 'O #>
let toLinq<'I,'O> (expr: Expr<'I -> 'O>) =
let linq = Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'I,'O>>(lambda.Body, lambda.Parameters)
let genrateRecordDeepCopyFunction<'T> () : ('T -> 'T) =
let expr = genRecordCopier typeof<'T>
let castExpr : Expr<obj -> obj> = expr |> Expr.Cast
let compiledExpr = (castExpr |> toLinq).Compile()
fun (v : 'T) -> compiledExpr.Invoke(box v) :?> 'T
I've tried several approaches, but I always get complaints about wanting (string -> string) but getting (obj -> obj) or wanting (object [] -> object []) but getting (string [] -> string []). Any ideas?
Here's a simple test case.
type SimpleArrayRecord = { Names: string array }
[<Fact>]
let ``record cloning should be able to clone a record with a simple array`` () =
let sr = { Names = [|"Rick"; "David"; "Mark"; "Paul"; "Pete"|] }
let func = RecordCloning.genrateRecordDeepCopyFunction<SimpleArrayRecord>()
let res = func sr
Assert.Equal(sr, res)
Here's the method that has gotten me the furthest. The issue seems to be that I can't get it to make the array typed, and so it always fails on the cast when trying to build the record. Adding a cast in the comprehension doesn't help.
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
<## fun (inobj: obj) ->
let arr = inobj :?> obj[] in
[| for i = 0 to arr.Length - 1 do yield (%%copyfun) (Array.get arr i) |] |> box ##>
Toyvo's solution below works for the example above but not for arrays of records:
type SimpleRecord = { Name: string; Age: int }
type LotsOfRecords = { People: SimpleRecord [] }
[<Fact>]
let ``record cloning should be able to clone a record with an array of records`` () =
let sr = { People = [|{Name = "Rick"; Age = 33 }; { Name = "Paul"; Age = 55 }|] }
let func = RecordCloning.genrateRecordDeepCopyFunction<LotsOfRecords>()
let res = func sr
Assert.Equal(sr, res)
For those who come later, here's the working code. I removed Option and haven't taken the time to clean it up but it's otherwise pretty decent.
let inline application prms expr = Expr.Application(expr, prms)
let inline coerse typ expr = Expr.Coerce(expr, typ)
let inline newrec typ args = Expr.NewRecord(typ, args)
let (|IsMapType|_|) (t: Type) =
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_,_>> then Some t
else None
let rec copyThing (mtype: Type) : Expr =
match mtype with
| _ when FSharpType.IsRecord mtype -> genRecordCopier mtype
| _ when FSharpType.IsUnion mtype -> genUnionCopier mtype
| _ when mtype.IsValueType || mtype = typeof<String> -> getIdFunc mtype
| _ when mtype.IsArray -> genArrayCopier mtype
| IsMapType t -> getIdFunc mtype
| _ when mtype = typeof<System.Object> -> getIdFunc mtype
| _ -> failwithf "Unexpected Type: %s" (mtype.ToString())
and X<'T> : 'T = Unchecked.defaultof<'T>
and getMethod =
function
| Patterns.Call (_, m, _) when m.IsGenericMethod -> m.GetGenericMethodDefinition()
| Patterns.Call (_, m, _) -> m
| _ -> failwith "Incorrect getMethod Pattern"
and getIdFunc itype =
let arg = Var("x", itype, false)
let argExpr = Expr.Var(arg)
let func =
let m = (getMethod <# id X #>).MakeGenericMethod([|itype|])
Expr.Call(m, [argExpr])
Expr.Lambda(arg, func)
and genRecordCopier (rtype: Type) : Expr =
let arg = Var("x", rtype, false)
let argExpr = Expr.Var(arg) //|> coerse rtype
let newrec =
FSharpType.GetRecordFields(rtype) |> Array.toList
|> List.map (fun field -> genFieldCopy argExpr field)
|> newrec rtype
Expr.Lambda(arg, newrec)
and genFieldCopy argExpr (field: PropertyInfo) : Expr =
let pval = Expr.PropertyGet(argExpr, field)
copyThing field.PropertyType |> application pval
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", atype, false)
let argExpr = Expr.Var(arg)
let func =
let m = (getMethod <# Array.map X X #>).MakeGenericMethod([|etype; etype|])
Expr.Call(m, [copyfun; argExpr])
Expr.Lambda(arg, func)
and genUnionCopier (utype: Type) : Expr =
let cases = FSharpType.GetUnionCases utype
// if - union case - then - copy each field into new case - else - next case
let arg = Var("x", utype, false)
let useArg = Expr.Var(arg)
let genCaseTest case = Expr.UnionCaseTest (useArg, case)
let makeCopyCtor (ci: UnionCaseInfo) =
let copiedMembers = [ for field in ci.GetFields() -> genFieldCopy useArg field ]
Expr.NewUnionCase(ci, copiedMembers)
let genIf ifCase thenCase elseCase = Expr.IfThenElse(ifCase, thenCase, elseCase)
let typedFail (str: string) =
let m = (getMethod <# failwith str #>).MakeGenericMethod([|utype|])
Expr.Call(m, [ <# str #> ])
let nestedIfs =
cases
|> Array.map (fun case -> genIf (genCaseTest case) (makeCopyCtor case))
|> Array.foldBack (fun iff st -> iff st) <| (typedFail "Unexpected Case in Union")
Expr.Lambda(arg, nestedIfs)
Now it actually works with unions as well. Cheers!
If you do this, make sure you understand generics and how to generate them. You are in LISP land, type system won't help you, since it cannot reason about itself - you are manipulating F# terms with F#.
and getMethod q =
match q with
| Patterns.Call (_, m, _) ->
if m.IsGenericMethod then
m.GetGenericMethodDefinition()
else
m
| _ -> failwith "getMethod"
and X<'T> : 'T =
Unchecked.defaultof<'T>
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
let wrappedLambda = coerseLambda etype copyfun
let func =
let m = getMethod <# Array.map X X #> // obtained (forall 'X 'Y, 'X[] -> 'Y[])
let m = m.MakeGenericMethod([| etype; etype |]) // specialized to 'E[] -> 'E[]
Expr.Call(m, [wrappedLambda; argExpr]) // now this type-checks
Expr.Lambda(arg, func)

How to return a type parameter that is a subtype of an Array?

I don't understand why this code is impossible in Scala:
def getColumns[T <: Array[_]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
Compiler says:
Expression of type Array[(String,Array[Double])] doesn't conform to expected type Array[(String, T)]
I have the same error with this code:
def getColumns[T <: Array[Double]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
It's more clear with my complete use case, finally I have chosen an Array[AnyVal]:
class SystemError(message: String, nestedException: Throwable) extends Exception(message, nestedException) {
def this() = this("", null)
def this(message: String) = this(message, null)
def this(nestedException : Throwable) = this("", nestedException)
}
class FileDataSource(path:String) {
val fileCatch = catching(classOf[FileNotFoundException], classOf[IOException]).withApply(e => throw new SystemError(e))
def getStream():Option[BufferedSource]={
fileCatch.opt{Source.fromInputStream(getClass.getResourceAsStream(path))}
}
}
class CSVReader(src:FileDataSource) {
def lines= src.getStream().get
val head = lines.getLines.take(1).toList(0).split(",")
val otherLines = lines.getLines.drop(1).toList
def getColumns():Array[(String,Array[_])] ={
val transposeLines = otherLines.map { l => l.split(",").map {
d => d match {
case String => d
case Int => d.toInt
case Double => d.toDouble
}}.transpose }
head zip transposeLines.map {_.toArray}
}
}
Can you give me some explanation or good links to understand the issues?
Because your function must be able to work with any T (any array type), however, it will always return an Array[(String,Array[Double])].
A simpler working signature would be:
def getColumns[T](): Array[(String,Array[T])]
But in the body of the function you will have to create an array of type T.
Your function signature requires a T in the compound return type, but you give it a Array[Double]. Note that T <: Array[Double], not the other way around. The following code works:
def getColumns[T >: Array[Double]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
Shouldn't be what you want but just for elaborating the problem.

Resources