Good evening, folks.
I recently encountered a need to define a structure to store a function and its related arguments into an instance.
struct Functor<Input, Output> {
//assuming only fuctions with one argument
let function: (Input) -> Output
let inputType: Input.Type
let outputType: Output.Type
let input: Input
let output: Output
init<Input, Output>(_ function: @escaping (Input) -> Output) {
self.function = function //there is already a compilation error
self.input = Input //???? And I have not a clue on how to store argument
}
}
Compilator says:
error: cannot assign value of type '(Input) -> Output' to type '(Input) -> Output'
The reason I need this is to define custom properties for functions with something like this:
protocol FunctorProtocol: Equatable, Hashable {
//assiming only functions with one argument
associatedtype Output
associatedtype Input
var function: (Input) -> Output { get set }
var name: String? { get set }
var inputType: Input.Type { get set }
var outputType: Output.Type { get set }
var preconditions: [(Input) -> Bool] { get set }
var postconditions: [(Output) -> Bool] { get set }
func callAsFunction(_ a: Input) throws -> Output
//func bellow is impossible to implement. Provide a stub
func isSemanticalyIdentical(to another: Self) -> Bool
//(...)->(?)
func hasSignature(identicalTo another: Self) -> Bool
//(...)
func hasArgumentSignature(identicalTo another: Self) -> Bool
func hasSameReturnType(as another: Self) -> Bool
init<Input, Output>(
name: String?,
preconditions: [(Input) -> Bool]?,
postconditions: [(Input) -> Bool]?,
inputType: Input,
outputType: Output,
function: (Input)->(Output)
)
}
To then implement bellow:
@CustomStorage(constraints: [{ $0 is Numeric }]) var someNumber: Any = 5 //can store any variable of type Numeric
try? assign(value: 0.5, to: someNumber) //success
try? assign(value: "String", to: someNumber) //failure
Can anybody, please, hint me, if I even doing this correct?
sveinhal
(Svein Halvor Halvorsen)
2
I think you’re shadowing the generic types Input and Output in your initializer. You don’t need to declare new genetic parameter types in it, you should use the ones inherited from the type.
1 Like
Thank you with your responce, I have fixed the code so it is working. But I actualy broke swift in other way
. I am using online swift compiler, to say.
Here is the minimal reproducible example
@Joe_Groff, could you please look
protocol DistilledFunctorProtocol {
//assiming only functions with one argument
associatedtype Output
associatedtype Input
var function: (Input) -> Output { get }
var inputType: Input.Type { get }
var outputType: Output.Type { get }
}
func isSignature<T: DistilledFunctorProtocol, U: DistilledFunctorProtocol>
(of some: T, identicalTo another: U) -> Bool {
return some.outputType == another.outputType &&
some.inputType == another.inputType
}
struct Functor<Input, Output>: DistilledFunctorProtocol {
//assuming only fuctions with one argument
let function: (Input) -> Output
let inputType: Input.Type
let outputType: Output.Type
var preconditions: [(Input)->Bool]
init(preconditions: [(Input)->Bool], _ function: @escaping (Input) -> Output) {
self.function = function
self.preconditions = preconditions
self.inputType = Input.self
self.outputType = Output.self
}
enum Unprocessable: Error { case preconditionError,
postconditionError
}
func invoke(with a: Input) throws -> Output {
for f in preconditions {
if f(a) == false { throw Unprocessable.preconditionError; break }
else { return self.function(a) }
}
}
}
let a = Functor<Int, Int>.init(preconditions: [{$0 > 10}]) { a in return a + 1 }
let b = Functor<Int, String>.init(preconditions: [{$0 != 0}]) { a in return String(a + 5) }
print(
isSignature(of: a, identicalTo: b),
try? a.invoke(with: 11)
)
It is not compiling and even report some wrong stuff happening at lower level.
swift: /home/buildnode/jenkins/workspace/oss-swift-5.1-package-linux-ubuntu-14_04/swift/lib/SILGen/SILGenConvert.cpp:556: virtual swift::SILValue (anonymous namespace)::ExistentialInitialization::getAddressForInPlaceInitialization(swift::Lowering::SILGenFunction &, swift::SILLocation): Assertion `!concreteBuffer && "concrete buffer already formed?!"' failed.
Stack dump:
0. Program arguments: /swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift -frontend -c -primary-file /tmp/B184149F-55C6-4333-91E1-7F0A9BBADE5D.spAatp/main.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /swiftplayground/OnlinePlayground/OnlinePlayground-5.1-RELEASE/.build/release -gnone -module-link-name Glibc -suppress-warnings -swift-version 5 -O -module-name SwiftPlayground -o /tmp/main-8f5821.o
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x45820b4]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x45800b0]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x4582418]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x10330)[0x7fd77608c330]
/lib/x86_64-linux-gnu/libc.so.6(gsignal+0x37)[0x7fd77486ec37]
/lib/x86_64-linux-gnu/libc.so.6(abort+0x148)[0x7fd774872028]
/lib/x86_64-linux-gnu/libc.so.6(+0x2fbf6)[0x7fd774867bf6]
/lib/x86_64-linux-gnu/libc.so.6(+0x2fca2)[0x7fd774867ca2]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x85cabf]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x887f90]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x87eaee]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x873898]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x88c9cf]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x85c502]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x849016]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x858804]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8849d4]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8705f2]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8dfd90]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8e0601]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x90b203]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x90bf6e]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x90a355]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x908b9c]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x907260]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x906818]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8f66ce]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x8f55b8]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x87e702]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x873db1]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x836389]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x836cbc]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x837eab]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x83830a]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x501544]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x4fe77c]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x4a2af2]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7fd774859f45]
/swiftplayground/Toolchains/swift-5.1-RELEASE.xctoolchain/usr/bin/swift[0x4a2708]
<unknown>:0: error: unable to execute command: Aborted
<unknown>:0: error: compile command failed due to signal 6 (use -v to see invocation)
Do you know what might be wrong?
There are two issues:
one is that print needs a simpler type in the invoke line
print(
isSignature(of: a, identicalTo: b),
(try? a.invoke(with: 11)) ?? 0
)
and the other is in the implementation of invoke; in case the array of preconditions is empty, this implementation has no return statement: you can fix it by tacking an extra return statement at the bottom of the function.
func invoke(with a: Input) throws -> Output {
for f in preconditions {
if f(a) == false { throw Unprocessable.preconditionError; break }
else { return self.function(a) }
}
return self.function(a)
}
About the isSignature function: why do you need a runtime check? You can turn it into a compile-time error using a where clause. At this point you don't even need this function anymore, as you can constraint the types of the rest of your logic.
func isSignature<
T: DistilledFunctorProtocol,
U: DistilledFunctorProtocol
>(
of some: T,
identicalTo another: U
) where T.Input == U.Input, T.Output == U.Output
{}
About the compiler error: I copy pasted your code in a Xcode 11.2.1 playground and I didn't get the compiler crash you got. 
Ok, got it. It is possible to specialize some cases of isSignature with where. But because some Functor types are generic and may be created at runtime, it is necessary to use more generalized solution, I think