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
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
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)))
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
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))
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.
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)))) ... )
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.