Idea: evolve special compilation mode used by main.swift/playgrounds into a special notation for defining a function?

I've lurked on this forum a bit but never posted. Hopefully this is the right place to discuss sort of half-baked ideas ... I did some searching on the forum but haven't found a lot of discussion that seemed related ...

A few years ago while learning swift I invested a few weeks trying to use swift playgrounds for data-sciencey tasks in a manner somewhat similar to how one might use jupyter notebooks. It wasn't a successful experience in the end -- but I still think there is potential for this workflow when/if playground's are expanded/better integrated into swift and (hopefully eventually) improved.

One issue I remember finding particularly annoying was the way it was essentially impossible to share code between a playground and swift application due to the fact that playgrounds allow/expect the use of top-level expressions while normal swift code is not allowed to use this construct except in main.swift.

A similar limitation preventing reuse exists within the ipython jupyter notebook community when one is using the standard tools. In that ecosystem, one writes code in a .ipynb file where the general expectation is generally that the code operates on the module scope and performs side-effects via top-level expressions. The similarity of this limitation is a bit obscured by the fact that python doesn't actually have syntax prohibited top-level expression and .ipynb files are horrendous json files rather than typical text but various tools exist to convert these files into .py files -- and when you do the conversion from a notebook to a .py module, you are left with the fact that code made for use in the notebook context is not parameterisable or reasonably re-useable outside of the notebook context without changes -- and there is no built-in way to make it so.

I find these playground/notebook programming models as implemented tend to force a trade-off -- in order to take advantage of the interesting features of a general purpose edit-eval-loop environment, one is forced to write code in a different style than one would typically use within an application -- there is usually not any clean way to write code which works both within this style (where it can be developed with values inspected interactively) while also being able to be directly re-used within an application or script.

Recently I was confronted with this trade-off again in the jupyter world and made a simple python module that models an ipython notebook as a function and allows one to invoke a notebook from a python program via a function call. I think modeling the interactive development environment as a function definition that can be immediately invoked without supplying external parameters is a pretty accurate fit to the programming model -- and if you actually do model the notebook environment this way, then it turns out you can easily re-use this code made for the notebook environment outside of the notebook environment within an application ...

Its gotten me thinking that something like this concept if applied to swift's implementation of playground's/main.swift might be a really good fit...

I'm imagining that the special flavor of swift allowed inside a playground/main.swift file (where top-level expressions are allowed) might be treated as a kind of syntax sugar for a function definition. To make the concept concrete (specifics are just for example) ...

Suppose a new file extension .swiftscript was added where top-level expressions were allowed. Expressions at top-level within the .swiftscript are allowed but interpreted as occurring within the body of an implicitly defined function with the same name as the file -- and its this function which is provided by the file to the outside world (the outside world being a containing swift module or whatever implicit module compilation context is created for use in playground runtime). Syntax could be defined to allow parameters to the function to be specified with an alternate syntax which required a name and default value to be supplied for all parameters -- this would ensure the function could be invoked in an interactive context without having to specify any external details -- say something like this:

In SomeFile.swiftscript

// exposes func SomeScript(someParam: String="defaultValue") { print("Do Something with \(someParam)" }
// if compiler support is added to infer the parameter name and type
let someParam = receiveParameter("defaultValue")
// or it could be more explicit ...
let someParam = receiveParameter(someParam: "defaultValue")
...
print("Do Something with \(someParam)!")

Return values could also be supported ...

This idea or something like it could help clarify what playground functionality actually is and might help establish a vision for the future ...:
(1) allow one to easily take a simple concept prototyped in a playground into a parameterized piece of reusable functionality - while still being able to drop back into the prototyping environment when desired
(2) re-interpreting playgrounds as an environment for invoking a specially instrumented function call could make it reasonable to support making this functionality 're-targetable' to any function within a project rather than only applying to playground bundles. It would be an awesome future if 'playground' became a re-targetable feature more like a debugger where one could provide parameters and 'invoke' any compatible function in a project in a 'playground mode' that allowed you to interactively develop the body ...

I think the existing facilities for handling the different syntax for top-level 'main.swift' and playground files are pretty disappointing in current form -- particularly the way that the design prevents sharing code between playgrounds and swift application's ...

I suspect presentation issues with my comment as primary reason why no one is interested to discuss this topic. I was not actually intending to propose something specific with the .swiftscript file extension example ... Lots of different ways to address the existing limitation ...

I suppose this thread doesn't (and won't) discuss anything actionable and only exists to document my hope that someday swift will be interested in making a generalized fast-feedback-prototyping-workflow into a language feature suitable for developer use and not just a custom application to support teaching the language ...

1 Like

I don't think anyone would object to that! I guess just as you said yourself, I had a hard time understanding the benefit as an end-user from reading your post.

Would your suggestions enable

  • using a Playground and drop in your framework of choice and then use the framework inside the playground?
  • write an application that can use input from a bundled playground, so I can play around with values in the playground and the app reacts?

Sry if those are dumb questions.

using a Playground and drop in your framework of choice and then use the framework inside the playground?

I think you can do this already -- but (in my understanding) what you can't do is get the Playground Inspectable experience from any of the code within the framework

write an application that can use input from a bundled playground, so I can play around with values in the playground and the app reacts?

Yeah that's what I'm after -- particularly for code within the top-level playground file. The other bundled code within the playground (regular .swift files) are directly shareable with an application/library -- however those code files do not receive the playground inspectable value treatment when invoked in the playground -- (unless my understanding is wrong) the only file that gets the full Playground Inspectable experience is the special file where the top-level expression syntax is allowed which cannot be directly invoked from an application/library

My (not well articulated) thought was that it seems like there is (always?) a kind of semantic substitutability between a function with default arguments and a playground file with the special top-level expression syntax -- or at least there must be a large subset of cases where this is so:

Simple (perhaps too simple) example: this function:

func someApplicationFunction(text: String="Pointless Example", count: Int=10) { 
  let exampleIntermediateState = String(count: count, repeatedValue: text)
}

Is equivalent to something like this playground:

// func someApplicationFunction(text: String="Pointless Example", count: Int= 10)
let text = "Pointless Example"
let count = 10
let exampleIntermediateState = String(count: count, repeatedValue: text)

Does this imply that it should be possible to support playground like interaction modes for function's with default arguments that exist within application/library targets?

My first notion was to expand the syntax allowed in the top-level playground context so that a playground could be invoked as a function inside an app target while also being interacted with as a playground -- something which is currently impossible because the syntax of a playground file is different from that which can be incorporated in any swift compilation context other than playground file's and main.swift.

If swift was extended so that the below someApplicationFunction.swiftscript file when compiled within a module produced a function func someApplicationFunction(text: String="Pointless Example", repeatedValue: Int= 10) that could be called:

let text = receive_parameter(text: "Pointless Example")
let count = receive_parameter(count: 10)
let exampleIntermediateState = String(count: count, repeatedValue: text)

This would allow one to interact with the body of a function in a playground environment while also invoking that function within an application ... This by itself would be useful as it would allow one to prototype functions inside playgrounds that could be directly invoked within applications/libraries.

Beyond that, my dream is that if playground's are just syntax sugar for a particular kind of function definition, then perhaps whatever special compilation support is needed to support the playground feature-set might also be suitable to apply to any compatible function defined normally ... It would be really cool if you could trivially enter an interactive development environment for any swift function in your codebase in a lightweight way simply by providing the minimal required information: 'run this function with these arguments'.

Perhaps clarifying the similarity between playground's and functions could ensure that the compilation behavior necessary to support playground functionality be made independent of the top-level expression syntax so that it could also be used for directly prototyping the body of normal function defined in a library/application.

2 Likes

That's quite well explained. I'll have to think about this a bit. But it sounds very interesting indeed!

1 Like

I meant to say this a whole week ago but please file a radar around this. This is more of an Xcode feature and, as such, swift-evolution is–in some ways–not the right place to make this happen.

EDIT: Rereading this, that might have seem more stern than intended. That first bit was just me admitting that I forgot to respond to this.