Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,56 @@ jobs:
run: |
nix develop --command zig build run-examples

# Test Swift All Challenges
test-swift-all-challenges:
name: Test Swift All Challenges
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Check if Swift implementation exists
id: check_swift
run: |
if [ -f "swift/metaprogramming-challenges/Package.swift" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi

- name: Install Nix
if: steps.check_swift.outputs.exists == 'true'
uses: DeterminateSystems/nix-installer-action@v13

- name: Setup Nix cache
if: steps.check_swift.outputs.exists == 'true'
uses: DeterminateSystems/magic-nix-cache-action@v7

- name: Remove system Swift from PATH
if: steps.check_swift.outputs.exists == 'true'
run: |
echo "PATH=$(echo $PATH | tr ':' '\n' | grep -v '/usr/local/bin' | tr '\n' ':')" >> $GITHUB_ENV

- name: Verify Swift version
if: steps.check_swift.outputs.exists == 'true'
working-directory: ./swift/metaprogramming-challenges
run: |
nix develop --command bash -c 'which swift && swift --version'

- name: Build Swift implementation
if: steps.check_swift.outputs.exists == 'true'
working-directory: ./swift/metaprogramming-challenges
run: |
nix develop --command bash -c 'swift build'

- name: Run Swift examples
if: steps.check_swift.outputs.exists == 'true'
working-directory: ./swift/metaprogramming-challenges
run: |
nix develop --command bash -c 'swift run StructuredLoggingExample'
nix develop --command bash -c 'swift run TypeReflectionExample'
nix develop --command bash -c 'swift run FunctoidExample'

# Verify all implementations
verify:
name: Verify All Tests
Expand All @@ -264,6 +314,7 @@ jobs:
- test-rust-functoid
- test-kotlin-functoid
- test-zig-all-challenges
- test-swift-all-challenges
steps:
- name: All tests passed
run: echo "All required tests passed successfully"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ We would like to see if the following can be done:
| **Kotlin** | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | [✅](kotlin/functoid) | [✅](kotlin/functoid) |
| **Haskell** | [✅](haskell/metaprogramming-challenges) | ❓ | [✅](haskell/metaprogramming-challenges) | ❓ | ➖ | ➖ | [✅](haskell/metaprogramming-challenges) | ⚠️ |
| **Zig** | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | [✅](zig/metaprogramming-challenges) | ⚠️ |
| **Swift** | [✅](swift/metaprogramming-challenges) | [⚠️](swift/metaprogramming-challenges) | [✅](swift/metaprogramming-challenges) | [⚠️](swift/metaprogramming-challenges) | [⚠️](swift/metaprogramming-challenges) | ❌ | [✅](swift/metaprogramming-challenges) | [⚠️](swift/metaprogramming-challenges) |
| **TypeScript** | ❌ | ❌ | ➖ | ⚠️ | ➖ | ❌ | ⚠️ | ⚠️ |

**Legend:**
Expand All @@ -123,6 +124,7 @@ We would like to see if the following can be done:
- **Haskell**: Template Haskell for basic variable capture (robust logger impossible - would require parsing Haskell syntax from strings); Typeable/Type.Reflection for runtime type identity; GADTs with type-level strings for functoids (parameter IDs via type-level strings, not annotations).
- **C++**: Structured logging includes robust variant (`LOG_ROBUST`) that handles arbitrary expressions using preprocessor stringification with try-catch fallback. All three challenges implemented using C++20 template metaprogramming, constexpr evaluation, and compiler intrinsics.
- **Zig**: Comptime execution for all challenges; `@typeName()` for pure library TypeId; `@typeInfo()` for complete function introspection; struct-based named parameters for logging; parameter IDs via compile-time arrays or runtime assignment.
- **Swift**: Mirror API for runtime reflection; KeyValuePairs or struct-based logging; TypeId via type name comparison; subtype checking only on macOS/iOS via ObjectiveC runtime; Functoid with manual parameter names (closures don't preserve parameter names at runtime).

### External Library Implementations

Expand Down
1 change: 1 addition & 0 deletions swift/metaprogramming-challenges/.envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
10 changes: 10 additions & 0 deletions swift/metaprogramming-challenges/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.build/
.swiftpm/
*.xcodeproj
xcuserdata/
DerivedData/
.DS_Store
*.swp
*.swo
*~
.envrc.cache
102 changes: 102 additions & 0 deletions swift/metaprogramming-challenges/Examples/Functoid/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import MetaprogrammingChallenges
import Foundation

print("=== Challenge 3: Functoid Concept ===\n")

// Example 1: Basic functoid introspection
print("Example 1: Basic functoid introspection")
let add = Functoid2(
id: "add",
paramNames: ["a", "b"],
function: { (a: Int, b: Int) -> Int in a + b }
)

print("Function: \(add.id)")
print("Arity: \(add.arity)")
print("Parameters:")
for (i, param) in add.parameterTypes.enumerated() {
print(" [\(i)] name: \(param.name), type: \(param.typeName)")
}
print("Return type: \(add.returnTypeName)")
print("Invocation: add(10, 32) = \(add.invoke(10, 32))")
print()

// Example 2: Functoid with mixed parameter types
print("Example 2: Functoid with mixed parameter types")
let greet = Functoid2(
id: "greet",
paramNames: ["name", "age"],
function: { (name: String, age: Int) -> Bool in age >= 18 }
)

print("Function: \(greet.id)")
print("Arity: \(greet.arity)")
print("Parameters:")
for (i, param) in greet.parameterTypes.enumerated() {
print(" [\(i)] name: \(param.name), type: \(param.typeName)")
}
print("Return type: \(greet.returnTypeName)")
print("Invocation: greet(\"Alice\", 25) = \(greet.invoke("Alice", 25))")
print()

// Example 3: Zero-parameter functoid
print("Example 3: Zero-parameter functoid")
let getAnswer = Functoid0(
id: "getAnswer",
function: { () -> Int in 42 }
)

print("Function: \(getAnswer.id)")
print("Arity: \(getAnswer.arity)")
print("Return type: \(getAnswer.returnTypeName)")
print("Invocation: getAnswer() = \(getAnswer.invoke())")
print()

// Example 4: Three-parameter functoid
print("Example 4: Three-parameter functoid")
let combine = Functoid3(
id: "combine",
paramNames: ["a", "b", "c"],
function: { (a: Int, b: Int, c: Int) -> String in "\(a)-\(b)-\(c)" }
)

print("Function: \(combine.id)")
print("Arity: \(combine.arity)")
print("Parameters:")
for (i, param) in combine.parameterTypes.enumerated() {
print(" [\(i)] name: \(param.name), type: \(param.typeName)")
}
print("Return type: \(combine.returnTypeName)")
print("Invocation: combine(1, 2, 3) = \(combine.invoke(1, 2, 3))")
print()

// Example 5: Heterogeneous collection using type erasure
print("Example 5: Heterogeneous collection of functoids")
let functoids: [AnyFunctoid] = [
add.eraseToAny(),
greet.eraseToAny(),
getAnswer.eraseToAny()
]

print("Collection of \(functoids.count) functoids:")
for f in functoids {
print(" - \(f.id): arity=\(f.arity), returns=\(f.returnTypeName)")
}
print()

// Example 6: Single-parameter functoid
print("Example 6: Single-parameter functoid")
let square = Functoid1(
id: "square",
paramNames: ["x"],
function: { (x: Int) -> Int in x * x }
)

print("Function: \(square.id)")
print("Arity: \(square.arity)")
print("Parameters:")
for (i, param) in square.parameterTypes.enumerated() {
print(" [\(i)] name: \(param.name), type: \(param.typeName)")
}
print("Return type: \(square.returnTypeName)")
print("Invocation: square(7) = \(square.invoke(7))")
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import MetaprogrammingChallenges
import Foundation

print("=== Challenge 1: Effortless Structured Logging ===\n")

// Example 1: Basic logging with KeyValuePairs
print("Example 1: Basic logging")
let entry1 = log("Hello {user}, your balance is {balance}", [
"user": "John",
"balance": 42
])
print(entry1.toJSON())
print()

// Example 2: Multiple types
print("Example 2: Multiple types")
let entry2 = log("User {name} is {age} years old with score {score} (active: {active})", [
"name": "Alice",
"age": 30,
"score": 95.5,
"active": true
])
print(entry2.toJSON())
print()

// Example 3: Using struct-based logging
print("Example 3: With computed values")
struct LogData {
let x: Int
let y: Int
let sum: Int
let product: Int
}

let data = LogData(x: 10, y: 5, sum: 15, product: 50)
let entry3 = logWithStruct("x={x}, y={y}, sum={sum}, product={product}", data)
print(entry3.toJSON())
print()

// Example 4: Different value types
print("Example 4: Different value types")
let entry4 = log("Status: {status}, Code: {code}, Success: {success}", [
"status": "OK",
"code": 200,
"success": true
])
print(entry4.toJSON())
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import MetaprogrammingChallenges
import Foundation

print("=== Challenge 2: Library-Based Reflection ===\n")

// Example 1: Type identity comparison
print("Example 1: Type identity comparison")
let intId1 = AnyTypeId(Int.self)
let intId2 = AnyTypeId(Int.self)
let stringId = AnyTypeId(String.self)

print("TypeId(Int) name: \(intId1.name)")
print("TypeId(Int) hash: \(intId1.hash)")
print("TypeId(String) name: \(stringId.name)")
print("TypeId(String) hash: \(stringId.hash)")
print("Int == Int: \(intId1.isSame(as: intId2))")
print("Int == String: \(intId1.isSame(as: stringId))")
print()

// Example 2: Different types
print("Example 2: Different types")
let boolId = AnyTypeId(Bool.self)
let doubleId = AnyTypeId(Double.self)

print("TypeId(Bool) name: \(boolId.name)")
print("TypeId(Double) name: \(doubleId.name)")
print("Int == Bool: \(intId1.isSame(as: boolId))")
print("Int == Double: \(intId1.isSame(as: doubleId))")
print()

// Example 3: Class hierarchy
print("Example 3: Class hierarchy and subtype checking")
class Animal {}
class Dog: Animal {}

let animalId = TypeId(Animal.self)
let dogId = TypeId(Dog.self)

print("Animal type: \(animalId.name)")
print("Dog type: \(dogId.name)")
print("Dog <: Animal: \(isSubtype(dogId, animalId))")
print("Animal <: Dog: \(isSubtype(animalId, dogId))")
print()

// Example 4: Type metadata
print("Example 4: Type metadata")
let intMeta = TypeMetadata(Int.self)
print("Int metadata:")
print(" name: \(intMeta.name)")
print(" size: \(intMeta.size)")
print(" alignment: \(intMeta.alignment)")
print(" is_class: \(intMeta.isClass)")
print(" is_struct: \(intMeta.isStruct)")
print()

struct Point {
let x: Int
let y: Int
}

let pointMeta = TypeMetadata(Point.self)
print("Point metadata:")
print(" name: \(pointMeta.name)")
print(" size: \(pointMeta.size)")
print(" alignment: \(pointMeta.alignment)")
print(" is_class: \(pointMeta.isClass)")
print(" is_struct: \(pointMeta.isStruct)")
print()

// Example 5: Runtime type info
print("Example 5: Runtime type information")
let point = Point(x: 10, y: 20)
let info = typeInfo(of: point)
print("Type: \(info.name), Kind: \(info.kind), Children: \(info.children)")
56 changes: 56 additions & 0 deletions swift/metaprogramming-challenges/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "MetaprogrammingChallenges",
platforms: [
.macOS(.v13),
.iOS(.v16),
.tvOS(.v16),
.watchOS(.v9)
],
products: [
.library(
name: "MetaprogrammingChallenges",
targets: ["MetaprogrammingChallenges"]
),
.executable(
name: "StructuredLoggingExample",
targets: ["StructuredLoggingExample"]
),
.executable(
name: "TypeReflectionExample",
targets: ["TypeReflectionExample"]
),
.executable(
name: "FunctoidExample",
targets: ["FunctoidExample"]
),
],
targets: [
.target(
name: "MetaprogrammingChallenges",
path: "Sources/MetaprogrammingChallenges"
),
.executableTarget(
name: "StructuredLoggingExample",
dependencies: ["MetaprogrammingChallenges"],
path: "Examples/StructuredLogging"
),
.executableTarget(
name: "TypeReflectionExample",
dependencies: ["MetaprogrammingChallenges"],
path: "Examples/TypeReflection"
),
.executableTarget(
name: "FunctoidExample",
dependencies: ["MetaprogrammingChallenges"],
path: "Examples/Functoid"
),
.testTarget(
name: "MetaprogrammingChallengesTests",
dependencies: ["MetaprogrammingChallenges"],
path: "Tests"
),
]
)
Loading
Loading