iScheme is a prototype implementation of ambient-oriented programming concepts that runs on iPhone devices. It provides developers with a convenient Scheme environment for constructing iPhone applications that exploit mobile platform capabilities such as sensors (accelerometer, and GPS), and Wi-Fi connectivity.
iScheme is built on top of an R5RS Scheme implementation that is developed at our lab. It supports Scheme and Objective-C interaction, thus enabling access to iPhone APIs (e.g., GPS, SMS, phone) from Scheme while bringing Scheme's well-known benefits (higher-order functions, structural macros, automatic garbage collection, etc.) to the iPhone development. iScheme provides a distribution layer that employs an event-driven style for peer-to-peer service discovery, asynchronous remote messaging, and timeout-based failure handling.
iScheme provides developers with an event-driven programming style for accessing iPhone capabilities as well as interacting with native applications, with higher-order functions being employed as event handlers. For example, retrieving location coordinates is achieved by way of the CURRENT-LOCATION
abstraction as follows:
(begin (CURRENT-LOCATION (lambda (lat longi) (display (list lat longi)) (newline)))) ;;(37.33168900 -122.0307310)
Scheme programs are directly executed on the iPhone and it is possible to write scripts that interact with native applications e.g., making a phone call by simply evaluating (make-call *phone-string*)
expression. The video here showcases the interactive Scheme environment on the iPhone.
When writing distributed iPhone applications in iScheme, the developer does not need to deal with low-level distribution concerns in Objective-C such as dealing with the Bonjour framework for service discovery, and socket APIs for remote communication. iScheme provides distribution constructs that abstract away these low-level distribution issues.
Let us demonstrate these constructs with an example: Consider a simple news service iPhone app that allows news editors to submit news articles to news publishers in the surroundings as they move about. Then, the news publisher broadcasts the news to nearby iPhone devices of potential customers that have announced their interest in the current news trends.
So, how do we realise such an application in iScheme? Read on…
In iScheme, distributed computation is expressed in terms of Scheme functions. A function represents a certain service offered by a device. A device can acquire a remote reference to a function owned by a remote device, and then interact with it by performing remote function invocations. As fixed name servers may not be available when two iPhones come in communication range and set up a collaboration, iScheme identifies exported functions by means of service types.
In the example of the news service application, the news publishers need to make available their publishing service to other devices. The code snippet below shows how a programmer can explicitly export the news-publisher
function representing the news publisher service.
(define news-service (service-type iPhone-news)) (export-service news-publisher news-service)
A service type is defined using the service-type
function. In the above code snippet, the variable news-service
stores the service type iPhone-news
. The export-service
function publishes onto the network a given function as a given service type. From the moment a function is exported, it is discoverable by functions residing in other devices by means of its associated service type. In this example, the news-publisher
function is exported on the network as a iPhone-news
service. The export-service
function returns a closure that can be used to take the function offline, by invoking the cancel-publication
function.
Service discovery in iScheme is by way of registering an event handler on a service type, which is triggered whenever a function exported under that type is encountered in the network. In the news service application, an editor can be notified whenever a news publisher is discovered as follows:
(when-discovered news-service (lambda (publisher-ref) (submit-news publisher-ref)))
The when-discovered
function takes as arguments the service type to search for and a one-parameter closure that serves as an event handler. Such a closure is invoked with a remote reference to the newly discovered remote function associated with that service type. In the above code snippet, whenever a iPhone-news
service is discovered, the submit-news
function is invoked, passing along the parameter publisher-ref
remote reference received. Similar to the export-service
function, the when-discovered
function returns a closure that can be used to cancel the subscription, by invoking cancel-subscription
function.
Once a reference to the remote function is obtained, remote function invocations can be performed by means of the remote-send!
function as follows:
(define (submit-news publisher-ref) (for-each (lambda (article) (remote-send! publisher-ref receive-article article)) list-of-articles))
The remote-send!
function takes as argument a remote reference, a function name, and optional variable number of arguments. In this example, the submit-news
function iterates over a list containing news articles to be published, and invokes the receive-article
function on the publish-ref
reference corresponding to the newly discovered news publisher.
The remote-send!
function performs a non-blocking asynchronous remote function call and it immediately returns nil
. As such, callers do not wait for the remote function call to be remotely performed nor for the return value of such computation.
In order to get the return value of a remote invocation, iScheme provides when-resolved
function which registers an event handler that is invoked when the return value of the remote function invocation becomes available. In our running example, this is used to acknowledge the reception of articles sent to the news publisher.
(define (submit-news publisher-ref) .... ;;iterator over news articles (when-resolved (remote-send publisher-ref receive-article article) (lambda (receipt) (set! receipts (cons receipt receipts)))) ... )
The remote-send
function works similar to the remote-send!
function but it returns a future instead. The when-resolved
function registers an event-handler which is executed when the future is resolved.
iScheme has been used to develop a couple of non-trivial iPhone applications.
iPhone apps developed in iScheme do not require any modifications to the iPhone OS, therefore, they can be deployed to the iPhone like any other third party apps. So far we have used Apple's iPhone Development Certificate to deploy applications to real devices. Even more good news is that, as of June 7th, 2010 the Apple iPhone Developer Program License Agreement stipulates that applications may embed interpreters, which means that iPhone applications developed in iScheme can be submitted to the App Store.