Swift Higher Order Function (Church Pair Aka Cons) with Generic Parameter Types Not Accepting Input Parameter Types

Higher order functions with generic argument in F#

The problem with your attempt is that you can't use operators + or * on parameters x and y, because it's not known that their type 'a has those operators defined.

To answer your further question in comments about how to achieve it anyway - if you want to use multiplication and addition on any type 'a that the caller chooses, you have to specify that. For an interface method, the only way to do this is by constraining the type parameter 'a, and the only two kinds of constraints that .NET runtime supports are "has a parameterless constructor" and "implements a given interface or inherits from a given class".

The latter one would be useful in your case: make both types implement the interface and then constrain type parameter 'a to implement that interface:

type IArithmetic<'a> =
abstract member add : 'a -> 'a
abstract member mult : 'a -> 'a

type T = Content of int
with
interface IArithmetic<T> with
member this.add (Content y) = let (Content x) = this in Content (x + y)
member this.mult (Content y) = let (Content x) = this in Content (x * y)

type W = { Content: int }
with
interface IArithmetic<W> with
member this.add y = { Content = this.Content + y.Content }
member this.mult y = { Content = this.Content * y.Content }

type I = abstract member Reduce<'a when 'a :> IArithmetic<'a>> : 'a -> 'a -> 'a
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// the constraint right here

...

let _ = f2 lt lw { new I with member __.Reduce x y = x.add y }
let _ = f2 lt lw { new I with member __.Reduce x y = x.mult y }

Is this a bit awkward? I guess so, but you're kind of doing the same thing for the SRTP version, so why not?

The core idea is: if you want your Reduce method to work not with just any type, but only with types that can do certain things, you have to specify what those things are. In the SRTP case you're doing that by defining the (+) and (*) operators. In the interface case you're doing that by implementing the interface.

Q: But can I make the interface somehow pick up the (+) and (*) operators?

A: In general, no. The .NET runtime just doesn't support the kind of constraints like "any type that has a method with certain signature". This means that such constraints can't be compiled down to IL, which means they can't be used in an interface implementation.

And this is the price you pay for using SRTPs: all those inline functions - they don't get compiled to IL, they always get expanded (inserted, substituted) at use sites. For small, simple functions, this is no big deal. But if your whole program is like that, you might see some unexpected compiled code bloat, potentially translating to slower startup time etc.


Having said all that, I must note that the code you're showing is toy POC kind of code, not intended to solve any real, practical problem. And as such, most musings on it are in danger of being completely useless.

If you have an actual problem in mind, perhaps try sharing it, and somebody would suggest the best solution for that specific case.

In particular, I have a nagging feeling that you might not actually need higher-rank functions (that's what it's called when a function doesn't lose genericity when passed as parameter).

How do I write the not/negate higher order function in swift?

You have two errors:

  • You're using A as both the type parameter and as the argument name.
  • You're using Any as the argument type instead of using the type parameter (A) as the argument type.

Try this:

func not<A>(predicate: @escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}

Note that in this version, I'm not using argument names for the predicate argument. You don't need an argument name in the declaration ((A) -> Bool) and I'm using the anonymous argument name ($0) in the body.


Okay, so you want to write this:

func isEmpty<T: Collection>(collection: T) -> Bool {
return collection.count == 0
}

func not<A>(_ predicate: @escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}

let notEmpty = not(isEmpty)

And you get this error:

let notEmpty = not(isEmpty)
^ Generic parameter 'A' could not be inferred

The problem is that this code tries to create a generic closure, but Swift doesn't support generic closures.

That is to say, what would the type of nonEmpty be? It would be something like:

<A: Collection>(A) -> Bool

and Swift doesn't support that.

Conflicting arguments to generic parameter 'T' ('[String : String]' vs. '[String]')

You are experiencing this error becuase in sendRequest's signature:

func sendRequest<T:Decodable>(toUrlPath urlPath: String, selection: Int, completion: @escaping (T, T)->Void)

in completion: @escaping (T, T) -> Void, you've specified that the completion would take in two arguments, both of type T but not different types even if they both conform to Decodable (that's what (T, T) means).

When you call sendRequest, in result: [String], you implicitly specified that T for sendRequest is [String], which leads the compiler to think that the second argument should be [String] (T) as well. However, in your call, you expected a [String : String] for the second argument, thus the compiler gave you the error.


If the types for your completion is fixed, you don't need generics here.

Change sendRequest to be

func sendRequest(toUrlPath urlPath: String, selection: Int, completion: @escaping ([String], [String : String])->Void)

Specify a generic value argument as a parameter of an object initialization call

If the intention is to have a (single) Event type with an EventValue property which can hold either an integer or a double value, then an enum with associated values would serve that purpose:

enum EventValue {
case ival(Int)
case dval(Double)
}

struct Event {
let time: Date
let value: EventValue
}

let event1 = Event(time: Date(), value: .dval(40.3467))
let event2 = Event(time: Date(), value: .ival(1234))

convert scheme lambda to swift closure

I wrote the following two functions for zero and add_1:

func zero<T>(f: T -> T) -> T -> T {
return { x in x }
}

func add_1<T>(n: (T -> T) -> T -> T ) -> (T -> T) -> T -> T {
return { f in
return { x in
return f(n(f)(x))
}
}
}

Now you can define one, for example, in terms of zero and add_1:

func one<T>(f: T -> T) -> T -> T {
return add_1(zero)(f)
}

Perhaps something like that is along the lines of what you are looking for?

And if you really want to use closures, it would look something like this, but it loses the ability to work with generics:

let _zero: (Int -> Int) -> Int -> Int = { _ in
return { x in x }
}

let _add_1: ((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int = { n in
return { f in
return { x in
return f(n(f)(x))
}
}
}

Why the same function prints different output?

Reviewing the code of my module and its incorrect behavior, the problem is not the way the print functions are defined (leaving out the tricks suggested by @Chris in his answer), but the module used: the Format module should be used to make more complex printouts (API reference), the formatting error is probably due to the arguments of the fmt string; for simpler printing, the Printf module and its printf function (same arguments) are more suitable.

Therefore a possible change to the printing functions is as follows:

let rec print_row = function
| [] -> ()
| c::cl -> Printf.printf "%2d " c; print_row cl;
and print_matrix m =
match m.c with
| [] -> ()
| rows ->
for i= 0 to (List.length rows)-1 do
print_string("[ ");
print_row (List.nth rows i);
print_string("]\n");
done;
;;

(In this case the field c (content) of type definition matrix is int list list to avoid conversions)

Functions on generic arrays

You want to cast the tuple Array of type [(index: Int, value: T)] where T is of type Reminder to a tuple Array of type [(index: Int, reminder: Reminder)]. So you can see that the tuples have different element names (value and reminder) where both have different byte sizes - therefore the error.

So you should take the same element names for both tuples.

If you want to change the element names:

let newTupleArray = oldTupleArray.map{ (newElementName0: $0.0, newElementName1: $0.1) }


Related Topics



Leave a reply



Submit