Semantic insertion for partial name replacement

Swift Playgrounds | Learn to Code 2 | Types | Corners of the World

I’d like to set the isActive status of both greenPortal and orangePortal by passing in their color name for use in the same expression (edit: invoked repeatedly for over multiple calls altering 1 of the 2 portals each time within a loop completing all tiles surrounding a given color of portal (please see code cite later)):

[color]Portal.isActive = false

Below is my current solve (with a screenshot of the puzzle)—managing orientation allows my simple loop to finish properly without further specification.

Having the “meta”-expression I seek would allow me to easily obviate all the shoe-leather that micro-manages between function calls. I’m open to ideas on how to improve my solution, but this post is primarily to ask if the type of semantic insertion I want is possible in Swift and if not why not, what I can/should do instead that’s a nearest approximation, and/or where I should look for what I’m after elsewhere in computing (Swift’s inout values as expounded by Sindell would seem to require 1st casting proxies for the subjects (in this case, the two portals), plus seems to deal only in properties or arguments whole, neither do I wish to predicate a reference upon inheritance or any other criterion)(I looked at LISP and Haskell in the past few days before posting this, but LISP doesn’t seem to handle branching very well at all given it insists upon returning as output a series’ final item, and Haskell doesn’t seem to ever splice its own statements within their names in the code like I’m hoping to do).

(thank you to @toph42 for the tip on formatting code here on Discourse)(please scroll within code cite to see full input):

var gathered = 0

func setGather(target: Int) {
    moveForward()
    while target > gathered {
        if isOnGem {
            collectGem()
            gathered += 1
        } else if isOnClosedSwitch {
            toggleSwitch()
            gathered += 1
        } else {
            turnLeft()
            turnLeft()
            moveForward()
            turnLeft()
            moveForward()
        }
    }
}


turnLeft()
moveForward()
moveForward() // through green portal
greenPortal.isActive = false
setGather(target: 4)
greenPortal.isActive = true
turnLeft()
turnLeft()
moveForward() // back through green portal
greenPortal.isActive = false
gathered = 0 // reset for next gathering batch
turnLeft()
turnLeft()
setGather(target: 2)
// shunting to the next set
turnLeft()
turnLeft()
moveForward()
moveForward()
moveForward()
moveForward()
moveForward() // through orange portal
orangePortal.isActive = false
gathered = 0 // reset for next set(/batch)
setGather(target: 4)
orangePortal.isActive = true
turnLeft()
turnLeft()
moveForward() // back through the orange portal
orangePortal.isActive = false
gathered = 0 // reset for next batch
turnLeft()
setGather(target: 2)

I don’t quite get what you’re asking. You want to be able to change both portals’ active state in one call?

You could do something like this and call these when you need:

func disableBothPortals() {
 greenPortal.isActive = false
 orangePortal.isActive = false
}

func enableBothPortals() {
 greenPortal.isActive = true
 orangePortal.isActive = true
}

The “same expression” meaning reused in successive calls of the same function. I apologize for being incomplete and/or unclear. By “the same” or “a single”, what I mean is that an argument I pass in to the function will serve as that portion of the objects’ names in a statement which sets their isActive property:

[passed-inArgumentString (e.g. “green”)]Portal.isActive = true

deployed as:

[portalColor]Portal.isActive = true

called, for example, via:

setGather(target: 4, portalColor: “green”)

Thank you for the submission/suggestion, but the reason that solution is unsatisfactory to me is because it requires nearly redundant expressions versus a single one that accepts a variable to identify which portal. FWIW, in subsequent lessons I decided to continue on to do, I made a toggleSwitches() using nearly the exact method you suggest, but would much prefer semantic insertion to avoid what in my view is needless code reiteration.

Performing common actions on similar properties can be achieved with KeyPaths. Rather than taking a String you can take a (Reference)WritiableKeyPath to determine which property you want to mutate.

1 Like

Having conducted an initial perusal, these sound very promising, thank you! I will report back soon.

Now that it is a little more clear to me what you meant, another possibility is a dictionary of portals:

let portals = ["green": greenPortal, "orange": orangePortal]

Now you can perform actions with the portals by pulling them out by color (portals["green"] or portals["orange"]) from the dictionary and you can also map actions.

portals.map {$0.value.isActive = true}

Note that this is allowed because Portal is a class and has reference semantics. If Portal was a value type like a struct or enum, this would fail as $0 would be a copy and not mutable.

Also note that the dictionary can't be sure you provided a valid key, so when you subscript it, you'll get an Optional<Portal> returned, so you'll need to make sure you're prepared for that.

I appreciate the suggestion and will look into it after KeyPaths since those seem nearly as direct as I’d wished.

I haven’t totally grokked using KeyPath yet, so it would be cool if you would post your solution with that when you have it.

For sure👍

1 Like

(please scroll within the code cite to see full input):

var gathered = 0

func setGather(initialOrient: String) {
    var targetPortal = greenPortal
    if initialOrient == "left" {
        targetPortal = greenPortal
    } else {
        targetPortal = orangePortal
    }
    func orientationTurn() {
        if initialOrient == "left" {
            turnLeft()
        } else {
            turnRight()
        }
    }
    func gather(target: Int) {
        gathered = 0
        moveForward()
        while target > gathered {
            if isOnGem {
                collectGem()
                gathered += 1
            } else if isOnClosedSwitch {
                toggleSwitch()
                gathered += 1
            } else { // core gather loop
                orientationTurn()
                orientationTurn()
                moveForward()
                orientationTurn()
                moveForward()
            }
        }
        orientationTurn()
        orientationTurn()
    }
    orientationTurn()
    moveForward()
    moveForward()
    targetPortal.isActive.toggle() // off for "X" gather
    gather(target: 4) // “X” gather
    targetPortal.isActive.toggle() // on to leave
    moveForward()
    targetPortal.isActive.toggle() // off for "L" gather
    orientationTurn()
    orientationTurn()
    gather(target: 2) // “L” gather
    moveForward()
    moveForward()
    moveForward()
    orientationTurn()
}


setGather(initialOrient: "left")
setGather(initialOrient: "right")

Swift’s built-in Bool function toggle()—once I discovered it exists—allowed me to construct an effectively posable expression, although I’d still prefer to use Key-Path because that would eliminate the need to define targetPortal and disambiguate its case via an if.

The reason I couldn’t use Key-Path is because I am starved of the class hierarchy the portal objects are made from by the fact that Swift Playgrounds displays the contents only of .swift files among the resources accessed via the “…” menu’s > Advanced > View Anciliary Source Files option (I have only my iPad, no Mac to fall back on into XCode to view them; I’m also not currently in a position to afford even the relatively modest Apple Developer membership fee to access any documentation that may be exclusive thereto which might have let me scaffold/hazard a guess as to what names would hit upon the portals’ valid path). The compiler disclosing via an error message one time that it couldn’t pass a Bool to object of type Portal was as close as I got, and a Key-Path of \Root.Portal.greenPortal.isActive gave an error that I had to “fill in this placeholder” (I’d include a screenshot but as a new user it says I can only include 1 media item per post :sweat_smile: ).

Compounding this are the facts that the Learn to Code 2 playground doesn’t seem to support alert() (I also tried show(), which apparently was what older version of Swift Playgrounds used instead), and that it effectively hides/covers-over console output (I really don’t feel it’s worthwhile to tackle grafting-in a separate view just for that), preventing me from discovering the portal’s path via String(describing: self).

I would still love to try a Key-Path solution, so if anyone’s willing to supply the full valid hierarchy of this lesson’s Portal type that should let me give it a whirl :pray: :crossed_fingers: Although, if I’m not mistaken, even optimally deployed, they are brokered by inheritance, which was something I hoped to avoid because it prevents granularity finer than whole names (I do note that the original proposal for Key-Path in its More Features section mentions, “creating a KeyPath from a String at runtime”, tantalizingly close to evaluating a path statement in the way I wish, e.g. [colorOf]Portal.isActive where [colorOf] is—perhaps similarly to the “\” initiating a Key-Path—dropped into the expression by the “[]” square brackets (no idea what use Swift may have claimed of those or therewith which would this conflict, just a hypothetical/provisional delimiter proposed for the sake of discussion, though one of some kind be requisite), passed in as a String argument yet part of an expression evaluating to a Bool (not 100% sure on all my terminology, here: part of a statement…?).

The reason I find acceptable my present solution is because I was able to exactly match the mirrored structure of the puzzle scenario with no hard-coded handling. The superiority to a Dict-based approach, though I never implemented it, seems to be several fold: greater clarity in settling disambiguation into the same, rather than casting out to separate, variables; no need for object index manipulation, such as by map(); and no complications introduced by having to pass in an argument purposely for portals per-se, versus my having just the 1 for all aspects of each iteration, encompassing also orientation management. True, I save, by my count, only 2 significant lines and 4 items over my original shoe-leather kludge, but the cleanliness of logic flows and function-to-code consummation are far higher and therefore, I find, much more satisfactory. I’m still open to ideas to improve it :grinning:

Please do let me try for a Key-Path solution by supplying this lesson’s portal objects’ full inheritance lineage!:grinning:

You can get a free Apple Developer account to look at documentation, WWDC videos, get downloads, etc..

1 Like

The Portal class has no supertype. You can see those, even on iPad, by viewing the source. Portal is defined in Portal.swift in the Book.playgroundmodule as public final class Portal: Item, NodeConstructible with no superclass and adopting two protocols also defined in the module.

1 Like

My search may have been exhausting, but was not exhaustive :blush: Thanks very much for the cite, @toph42 ! :pray: :+1: I’ll see what I can come up with, soon :slightly_smiling_face:

1 Like

Well, no wonder it wouldn’t work! Access is blocked. I finally got slapped in the face with the fact by the compiler once I finally completed the Key-Path. I imagine it’s to protect the lesson files so that students don’t accidentally hoark themselves out of the ability to progress, but what a bummer. I was able to get Key-Path-based assignment happening in my own playground, though, and used it both directly with the built-in Bool’s toggle() as well as routed through a function as an inout argument. I’m honestly not interested in attempting to circumvent the internal protection blocking Portal instance property access via Key-Path, at this point, given I’ve been able to exhibit a functioning deployment of them on my own. Since I’m too new to post multiple images I assembled the following 4 into 1:

  1. inout argument proof-of-concept, independently of Key-Path
  2. Key-Path-based value alteration proof-of-concept in my own from-scratch playground, scaffolded from the 1st example of them under Swift.org > The Swift Programming Language > Language Reference > Expressions > Key-Path Expression
  3. the Portal.swift anciliary source file’s construction of the isActive property allowing me to build the full Key-Path
  4. the brick wall of Learn to Code 2 preventing access to the Portal type’s properties

I hope this is helpful for those such as @toph42 interested in KeyPath. Thanks especially to @Jon_Shier for suggesting I try it; it’s too simple an example to need WriteableKeyPath, but having researched it I feel much better poised to employ that should occasion present itself. Key-Path’s greatest strength, seems to me from this preliminary vantage point, is that because it deals in properties only in determining its destination, separately from consideration of whatever value is at that destination, Key-Path’s effective path can, particularly through ReferenceWritableKeyPath, be made dynamically responsive, such as to properties brought about during runtime, not part of an object’s initial definition or, failing that, their destinations rendered mutable (in addition, obviously, to the values stored at their destinations).

My desire for a spliceable, string-based evaluation of them could still be kept type-safe, seems to me, because each node still must resolve to a valid element, it’s just that in that evaluation process would be a preliminary pass resolving locally-derived partial-substitutions, e.g. the example beginning this thread of [color]Portal.isActive where color is a function argument, semantically inserted for partial name replacement. This kind of manipulation could be conducted upon the paths from within their handler variables where they’re stored as values, but I would welcome/prefer the more self-contained and, in its way, more direct capability I desire: to be made accessible within a local scope it must first be separately defined then assigned to a dedicated variable, whereas semantic insertion allows direct construction in-place and direct use of that construction as an actionable statement. If others agree, I’d love to hear whether or not you think there’s merit in proposing it formally as an Evolution of/for Swift.

1 Like