How to use buildExpression in Swift 5.2 Function Builders? - swift

I understand that it's a draft proposal. I tried to implement a simple DSL for building a string, like so:
#_functionBuilder
struct StringBuilder {
static func buildExpression(_ string: String) -> [String] {
[string]
}
static func buildBlock(_ children: [String]...) -> [String] {
children.flatMap{ $0 }
}
}
func s(separator: String = "", #StringBuilder _ makeString: () -> [String]) -> String {
makeString().joined(separator: separator)
}
let z = s(separator: " ") {
"this"
"is"
"cool"
}
However, the compiler complains that "'String' is not convertible to '[String]'". This leads me to believe that buildBlock is the only part of the proposal currently implemented. (This is understandable given that in SwiftUI they are building a hierarchy of views, so that's all they need.)
Is this correct or am I doing something wrong? What is the correct way to use buildExpression?
ielyamani's answer shows how to build a working string builder such as I used in my example above. However, that does not solve the actual problem. I'm not trying to build a string builder. I'm trying to figure out function builders. The string builder is just an example. For example, if we wish to have a string builder that accepts integers, we could in theory do the following:
#_functionBuilder
struct StringBuilder {
static func buildExpression(_ int: Int) -> [String] {
["\(int)"]
}
// The rest of it implemented just as above
}
In this case, when the compiler encountered an Int, it would call buildExpression to then spit out our component type, in this case [String]. But as Martin R said in a comment to this question, buildExpression is not currently implemented.

I encountered the same issue today, it seems that buildExpression isn't implemented. I ended up making a workaround by using a protocol "ComponentProtocol" and then creating "Expression: ComponentProtocol" and "Component: ComponentProtocol". That works for me for now. I am hoping it'll be implemented later.
protocol ComponentProtocol: ExpressibleByIntegerLiteral, ExpressibleByStringLiteral {
var value: String { get }
}
struct Expression: ComponentProtocol {
let _value: String
var value: String { _value }
init(_ value: String) { _value = value }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) {_value = String(describing: value) }
}
struct Component: ComponentProtocol {
let _values: [String]
var value: String { _values.joined(separator: ", ") }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) { _values = [String(describing: value)] }
init<T: ComponentProtocol>(_ values: T...) { _values = values.map { $0.value } }
init<T: ComponentProtocol>(_ values: [T]) { _values = values.map { $0.value } }
}
#_functionBuilder struct StringReduceBuilder {
static func buildBlock<T: ComponentProtocol>(_ components: T ...) -> Component { Component(components) }
static func buildEither<T: ComponentProtocol>(first: T) -> Component { Component(first.value) }
static func buildEither<T: ComponentProtocol>(second: T) -> Component { Component(second.value) }
static func buildOptional<T: ComponentProtocol>(_ component: T?) -> Component? {
component == nil ? nil : Component(component!.value)
}
}
func stringsReduce (#StringReduceBuilder block: () -> Component) -> Component {
return block()
}
let result = stringsReduce {
Expression(3)
"one"
Expression(5)
Expression("2")
83
}
let s2 = stringsReduce {
if .random () { // random value Bool
Expression(11)
} else {
Expression("another one")
}
}

Since buildBlock(_:) takes a variadic number of arrays of strings, this would work:
let z = s(separator: " ") {
["this"]
["is"]
["cool"]
}
But that's still clunky. To take strings instead of Arrays of strings, add this function to StringBuilder which takes a variable number of strings:
static func buildBlock(_ strings: String...) -> [String] {
Array(strings)
}
And now you can do this:
let z = s(separator: " ") {
"Hello"
"my"
"friend!"
}
print(z) //Hello my friend!

Related

Typescript Generic chained function to Swift

I'm really struggling to comprehend how to (if even possible) to convert a generics function written in Typescript into something I can use in Swift.
export type Filter<T> = (value: T) => boolean
export function isKeyEqualToValue<T>(key: keyof T) {
return function (value: T[keyof T]): Filter<T> {
return (object: T) => object[key] === value
}
}
isKeyEqualToValue<T>('key')(someObject.key)
const filters = userFilters.map(userFilterSet => isEvery(buildAlertFilter(userFilterSet)))
const isMatch = isAny(filters)
return flow.reduce((feed: String[], obj: SomeType) => {
if (!isMatch(obj)) return feed
return [
...feed,
{
...obj
},
]
}, [])
}
I would like to be able to input a struct model in for T and check if the inputted value matches the key. Would greatly appreciate some guidance here!
EDIT:
I've added how the method is being called and used. Essentially I'm trying to avoid doing an algorithm O(n)^2 and so I'm trying to build a list of filters based on our user's choice. Then cross check the bulk of my data (SomeType) with those built filters.
I'm working to translate another function using the similar principles.
export function hasInArray<T>(key: keyof T) {
return function (values: Array<any>): Filter<T> {
return (object: T) => values.includes((object[key] as unknown) as string)
}
}
This is what I have so far.
func notInArray<Root, Value>(for keyPath: KeyPath<Root, Value>) -> (Array<Any>) -> Filter<Root, Value> {
{ values in { object in !values.contains(where: object[keyPath: keyPath]) } } }
You haven't given how you expect to use this, so I need to make some assumptions. I'm assuming the TypeScript that calls this looks like this:
interface Person {
name: string;
age: number;
}
const key: keyof Person = "name";
const nameTester = isKeyEqualToValue(key);
const person = {name: "Alice", age: 23};
const result = nameTester("Alice")(person);
The equivalent to TypeScript's keyof in Swift is KeyPath. Keeping this as close to the TypeScript syntax as possible to make it easier to see how it maps, this would look like:
typealias Filter<T> = (_ value: T) -> Bool
func isKeyEqualToValue<T, Value>(key: KeyPath<T, Value>) -> (Value) -> (T) -> Bool
where Value: Equatable
{
return { (value: Value) -> Filter<T> in
return { (object: T) in object[keyPath: key] == value }
}
}
struct Person: Equatable {
var name: String
var age: Int
}
let key = \Person.name
let nameTester = isKeyEqualToValue(key: key)
let person = Person(name: "Alice", age: 23)
let result = nameTester("Alice")(person)
To make it better Swift (rather than matching the TypeScript so closely), it would look like:
typealias Filter<Root, Value: Equatable> = (Value) -> (Root) -> Bool
func isEqualToValue<Root, Value>(for keyPath: KeyPath<Root, Value>) -> Filter<Root, Value>
{
{ value in { object in object[keyPath: keyPath] == value } }
}
let nameTester = isEqualToValue(for: key)
Your second example is like the first.
func hasInArray<Root, Values>(for keyPath: KeyPath<Root, Values>) -> (Values.Element) -> (Root) -> Bool
where Values: Sequence, Values.Element: Equatable
{
{ value in { object in object[keyPath: keyPath].contains(value) } }
}
You will almost never want Array<Any>. You need an array of the specific element. But in this case you don't need an array at all; you just need any Sequence.
All this said, I wouldn't do it this way. I think it's much easier to understand if you create a Filter type to manage it.
// A Filter object over a specific Target object (for example, a Person)
struct Filter<Target> {
let passes: (Target) -> Bool
}
// Filters can be created many ways
extension Filter {
// By properties equal to a value
static func keyPath<Value>(_ keyPath: KeyPath<Target, Value>, equals value: Value) -> Filter
where Value: Equatable
{
Filter { target in
target[keyPath: keyPath] == value
}
}
// By properties containing a value
static func keyPath<Seq>(_ keyPath: KeyPath<Target, Seq>, contains value: Seq.Element) -> Filter
where Seq: Sequence, Seq.Element: Equatable
{
Filter { target in
target[keyPath: keyPath].contains(value)
}
}
// By a property being a member of a sequence
static func keyPath<Seq>(_ keyPath: KeyPath<Target, Seq.Element>, isElementOf seq: Seq) -> Filter
where Seq: Sequence, Seq.Element: Equatable
{
Filter { target in
seq.contains(target[keyPath: keyPath])
}
}
// By combining other filters
static func all(of filters: [Filter]) -> Filter {
Filter { target in
filters.allSatisfy { filter in filter.passes(target) }
}
}
}
struct Person {
var name: String
var age: Int
var children: [String]
}
let filter: Filter<Person> = .all(of: [
.keyPath(\.name, equals: "Alice"),
.keyPath(\.children, contains: "Bob"),
.keyPath(\.age, isElementOf: [23, 43]),
])
let alice = Person(name: "Alice", age: 23, children: ["Bob"])
let shouldInclude = filter.passes(alice) // true

how to store away sequence variables with constraints in swift

I wanted to create a "where_non_null" operation that works on any swift sequence - which is easy if you return an array, but obviously that is potentially bad performance wise - because you are forcing the entire sequence to resolve in memory - so I created the following that just goes line by line:
//
// this iterates through the underlying sequence, and returns only the values that are not null
//
public class Not_null_iterator<T> : IteratorProtocol
{
public typealias Element = T
private let next_function : () -> T?
init<T_iterator: IteratorProtocol>( _ source: T_iterator ) where T_iterator.Element == Optional<T>
{
var iterator = source
next_function =
{
while (true)
{
if let next_value = iterator.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
else
{
return nil
}
}
}
}
public func next() -> T? {
next_function()
}
}
//
// a sequence wrapping an underlying sequence, that removes any nulls as we go through
//
public class Not_null_sequence<T > : Sequence
{
private var iterator_creator : () -> Not_null_iterator<T>
init<T_source_sequence : Sequence >( _ source : T_source_sequence ) where T_source_sequence.Element == Optional<T>
{
iterator_creator =
{
Not_null_iterator(source.makeIterator())
}
}
public func makeIterator() -> Not_null_iterator<T>
{
iterator_creator()
}
}
extension Sequence
{
//
// return only the not null values in the sequence without ever resolving more than one item in memory at one time and remove the optionality on the type
//
func where_not_null<T>() -> Not_null_sequence<T> where Element == Optional<T>
{
return Not_null_sequence( self)
}
}
class Where_not_null_tests : XCTestCase
{
public func test_where_not_null()
{
let source = [1, 2, 3, nil, 4]
let checked : [Int] = Array(source.where_not_null())
XCTAssertEqual([1,2,3,4],checked)
}
}
which works great - however I had to define the next() and make_iterator() functions in the constructor, because I couldn't find any type safe way of putting the source into a class level variable.
Is there a way of doing that?
[and yes, I'm aware swift people prefer camel case]
Rather than just using one generic parameter, you'd need two generic parameters. You can't just constrain one generic parameter to say that it has to be some sequence with an element of some Optional. You need another generic parameter to say what the optional's type is:
class NotNilIterator<T: IteratorProtocol, U>: IteratorProtocol where T.Element == U? {
typealias Element = U
var iterator: T
init(_ source: T) {
iterator = source
}
func next() -> Element? {
// I feel this is clearer what is going on
while true {
switch iterator.next() {
case .some(.none):
continue
case .none:
return nil
case .some(.some(let element)):
return element
}
}
}
}
class NotNilSequence<T: Sequence, U> : Sequence where T.Element == U?
{
let sequence: T
init(_ source : T)
{
sequence = source
}
public func makeIterator() -> NotNilIterator<T.Iterator, U>
{
.init(sequence.makeIterator())
}
}
whereNotNil would then be declared like this:
func whereNotNil<T>() -> NotNilSequence<Self, T> where Self.Element == T?
{
return .init(self)
}
Note the use of self types. The first parameter is the type of the underlying sequence, the second is the non-optional type.
Note that this sort of "lazily computed sequence" is already built into Swift. To lazily filter out the nils, do:
let array = [1, 2, 3, nil, 4]
let arrayWithoutNil = array.lazy.compactMap { $0 }
The downside is that the type names are quite long. arrayWithoutNil is of type
LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[Int?]>.Elements, Int?>>, Int>
But you can indeed get non-optional Ints out of it, so it does work.
The way swift generics work can sometimes be very confusing (but has it's advantages). Instead of declaring that a variable is of a generic protocol (resp. a protocol with associated types), you instead declare another generic type which itself conforms to your protocol. Here's your iterator as an example (I have taken the liberty to clean up the code a bit):
public class Not_null_iterator<T, T_iterator> : IteratorProtocol where
T_iterator: IteratorProtocol,
T_iterator.Element == Optional<T>
{
private var source: T_iterator
init(_ source: T_iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
The non-null sequence works analogous:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Not_null_iterator<T, Source.Iterator> {
Not_null_iterator(self.source.makeIterator())
}
}
Using this some IteratorProtocol is just a nice way to let the compiler figure out the type. It is equivalent to saying Not_null_iterator<T, Source.Iterator>
As a (potentially) interesting side-note, to clean up the generic mess even more, you can nest the iterator class inside the Not_null_sequence:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Iterator{
Iterator(self.source.makeIterator())
}
public class Iterator: IteratorProtocol {
private var source: Source.Iterator
init(_ source: Source.Iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
}

Using a Function Argument as a Dynamic Variable

Is there any way to make this work without resorting to calling the subscript directly?
#dynamicMemberLookup
final class Fuzzy {
private var backing: [String: String] = [:]
subscript(dynamicMember key: String) -> String? {
get {
return backing[key]
}
set {
backing[key] = newValue
}
}
}
let fuzz = Fuzzy()
func worksFine() {
fuzz.hello = "hi :)"
print(fuzz.hello!)
}
// *** HERE WOULD LIKE TO PASS ARG DYNAMICALLY
func canThisWork(with arg: String) {
print(fuzz\.arg)
}
You can probably use KeyPath if you only want to access the value:
func canThisWork(with arg: KeyPath<Fuzzy, String?>) {
print(fuzz[keyPath: arg]!)
}
Or WritableKeyPath if you also want to mutate it:
var fuzz = Fuzzy()
func canThisWork(with arg: WritableKeyPath<Fuzzy, String?>) {
fuzz[keyPath: arg] = "hi :)"
print(fuzz[keyPath: arg]!)
}
In both cases you call your function like this:
canThisWork(with: \.hello)

using functions in swift

I'm new to programming. Please tell me what is wrong in this code! Why am I getting the output "(Function)"?
//first function
func admit(person: String) -> String {
return("\(person) can go")
}
//second function
func deny(person: String) -> String {
return("\(person) can not go")
}
//third function
func screen(onGuestList: String, person: String) -> (String) -> String {
if onGuestList == "yes"{
return admit(person:)
} else {
return deny(person:)
}
}
var outcome = screen(onGuestList: "yes", person: "Sapinder")
print(outcome)
I expect the output of "(person) can go", but the actual output is "(Function)".
Why am I getting the output "(Function)"?
because screen function is not returning a String, it returns (String) -> String instead.
Simply, the fix for it is to implement screen as:
func screen(onGuestList: String, person: String) -> String {
if onGuestList == "yes"{
return admit(person: person)
} else {
return deny(person: person)
}
}
so what is the difference here? Well, first of all now it returns a string instead of a function that takes a string and returns a string. Also, for calling admit and deny you have to mention the label (person) to pass a parameter to them.
Unrelated tip:
func screen(onGuestList: String, person: String) -> String {
return onGuestList == "yes" ? admit(person: person) : deny(person: person)
}
preferably, try to name the functions as verbs instead of nouns, we usually do this for properties (fields) but not methods (behaviors). For example: displayScreen instead of screen.
So what's the meaning of returning (String) -> String?
Briefly, Swift does allow such a thing. Consider the following example:
func sayHello() -> (String) -> String {
let functionToReturn: (String) -> String = { name in
return "Hello \(name)"
}
return functionToReturn
}
func takeMy(function: (String) -> String, name: String) {
print("I am about to print the function:")
print(function(name))
}
takeMy(function: sayHello(), name: "Sappie")
// I am about to print the function:
// Hello Sappie
as you can see, takeMy function is that takes another function as a parameter of type (String) -> String, therefore we passed sayHello() for it since it's signature matches the parameter type.
As a real world example, you could find many methods that parameters as functions when working with collections (for instance). As an example, the filter method:
func returnMoreThanfive(element: Int) -> Bool {
return element > 5
}
let array = [1,2,3,4,5,6,7,8,9]
let filteredArray = array.filter(returnMoreThanfive)
// [6, 7, 8, 9]
we passed to filter a function that takes an element and returns a boolean. Keep in mind It's just an example to make it more clear to you, however we usually do like this:
let filteredArray = array.filter { $0 > 5 }
Try
func admit(person: String) -> String {
return("\(person) can go")
}
//second function
func deny(person: String) -> String {
return("\(person) can not go")
}
//third function
func screen(onGuestList: String, person: String) -> String {
if onGuestList == "yes"{
return admit(person: person)
} else {return deny(person: person)
}
}
var outcome = screen(onGuestList: "yes", person: "Sapinder")
print(outcome)
What you were doing were returning a (String) -> String instead of String
In swift you can return a Function as a return type

How to work generic with ranges?

My intention is the following:
My first function:
public func substringsOfLength(_ length: Int, inRange range: CountableClosedRange) -> Array<String>
{
...
}
And my second:
public func substringsOfLength(_ length: Int, inRange range: CountableRange) -> Array<String>
{
...
}
How can I realize both of them in one function? I know that Ranges are structures, so I can't use generalization paradigm. And I know too, that CountableRanges conform to RandomAccessCollection protocol and the bounds of them to Comparable, _Strideable and SignedInteger (Bound.Stride). Consequently, I search for a generic solution, right?
So I tried something like that:
public func substringsOfLength<T: RandomAccessCollection>(_ length: Int, inRange range: T) -> Array<String>
{
...
}
I know that here are the other protocols missing, but I don't know how to concretize the bounds with them.
I will try a little bit different and more "Swifty" approach ...
import Foundation
let str = "1234 567 890 1 23456"
extension String {
subscript(bounds: CountableClosedRange<Int>) -> String {
get {
return self[self.index(self.startIndex, offsetBy: bounds.lowerBound)...self.index(str.startIndex, offsetBy: bounds.upperBound)]
}
}
subscript(bounds: CountableRange<Int>) -> String {
get {
return self[bounds.lowerBound...bounds.upperBound]
}
}
var count: Int {
get {
return self.characters.count
}
}
func substrings(separatedBy: CharacterSet, isIncluded: (String) -> Bool)->[String] {
return self.components(separatedBy: separatedBy).filter(isIncluded)
}
}
let a0 = str[2..<14].components(separatedBy: .whitespacesAndNewlines).filter {
$0.count == 3
}
let a1 = str[2...13].substrings(separatedBy: .whitespacesAndNewlines) {
$0.count == 3
}
prints
["567", "890"] ["567", "890"]
Very soon the String will be the collection of characters and life will be easier ... (then just remove a part of your code)
As you can see, the function substrings is almost redundant and probably is better to remove it.