F# and DSS

So I rushed ahead of myself a bit and though that I'd try and implement a DSS service in F#. I've posted below what I think is the equivalent code for the default DSS service you get when you create a new DSS simple service project. There are three salient points:

  1. I've collapsed the *Types and *Service into a single file.
  2. The support for CCR iterators in the handler methods is a little bit of a hack (my hack).
  3. It doesn't work. It doesn't even compile.

(3) is clearly the most salient point. DSS requires that the Contract class in a service namespace expose a string literal called 'Identifier' that contains the contract URI. Unfortunately, the code listed below doesn't expose a string literal but rather a read-only property. CLR aficionados will know that the difference is that clients that refer to 'Contract.Identifier' via a literal end up with that literal in their own assembly so that if that literal changes in the source assembly, pre-existing dependent assemblies don't reflect that change.

[BTW, don't change contract identifiers like this. Treat them as web-shaped GUIDs].

Unfortunately, F# does not currently support const literals. So you cannot currently write DSS services in F#. Which is a shame because I think the code could be a slight improvement on the C# equivalent.

Incidentally, it doesn't compile simply because of the use of Contract.Identifier in the ContractAttribute. As a read-only property, this is a runtime evaluation, not a compile-time one so cannot be used in an attribute constructor. This can be fixed by just replacing the symbolic property with its literal value. However, the DSS runtime still fails to find a Contract class of the right shape.

There are some differences also in the implementation of the handler methods, due to the current limitations of computation expressions, the primary one being (approximately) that such expressions can only yield results directly - you can't write 'regular' code between yields. The workaround for this is to yield functions that contain the regular code and each return an ITask, then have another computation expression that yields the result of those functions. The following snippets from an F# file of CCR helpers illustrate this:

#light

open System
open Microsoft.Ccr.Core

let deref (x : seq<unit -> ITask>) = (seq { for f in x do yield f() }).GetEnumerator()
let handler x = Arbiter.FromHandler(new Handler(x))
let receive<'a> persist (port : Port<'a>) handler = 
Arbiter.Receive(persist, port, new Handler<'a>(handler))
let receive_one<'a> (port : Port<'a>) = receive false port
let receive_many<'a> (port : Port<'a>) = receive true port
let receive_with_iterator<'a> persist (port : Port<'a>) handler =
Arbiter.ReceiveWithIterator(persist, port, new IteratorHandler<'a>(fun(a) ->
deref(handler(a))))
let receive_one_with_iterator<'a> (port : Port<'a>) =
receive_with_iterator false port
let receive_many_with_iterator<'a> (port : Port<'a>) =
receive_with_iterator true port

let activate_many taskQueue tasks =
Arbiter.Activate(taskQueue, [| for t in tasks -> t :> ITask |] )

let activate taskQueue task =
activate_many taskQueue [| task |]

let ( &= ) taskQueue task =
activate taskQueue task

And here's an example of using them

let port = new Port<int>()
taskQueue &= (receive_many_with_iterator port (fun i -> 
print_endline ("Triggered.")
seq {
yield fun() ->
receive_one port (fun i -> print_endline (i.ToString()))
yield fun() ->
receive_one port (fun i -> print_endline (i.ToString()))
}))

port.Post(1)
port.Post(2)
port.Post(3)

 


Anyway, I digress. Here's the code for the service. My F# experience is minimal, so I'd be happy to hear about any improvements to any of this code.

#light

namespace Iodyne.Drivers.Tcp.Connection

open System
open System.ComponentModel
open System.Net
open System.Net.Sockets
open Microsoft.Ccr.Core
open Microsoft.Dss.Core.Attributes
open Microsoft.Dss.ServiceModel.Dssp
open Microsoft.Dss.ServiceModel.DsspServiceBase;
open W3C.Soap

open Ccradaptors;

type Contract = class
static member Identifier = "http://iodyne.blogspot.com/2007/10/driver/tcp/connection"
end

[<DataContract>]
type State = class
new() = {}
end

type
Get = class
inherit Get<GetRequestType, PortSet<State, Fault>>
new() = {}
new(body : GetRequestType) =
{ inherit Get<GetRequestType, PortSet<State, Fault>>(body) }
new(body : GetRequestType, response : PortSet<State, Fault>) =
{ inherit Get<GetRequestType, PortSet<State, Fault>>(body, response) }
end

[<ServicePort>]
type Operations = class
inherit PortSet<DsspDefaultLookup, DsspDefaultDrop, Get>
new() = {}
end

[<DisplayName("TcpConnection");
Description("TcpDriver Connection Service");
ContractAttribute(Contract.Identifier)>]
type Service = class
inherit DsspServiceBase as base
[<ServicePort(AllowMultipleInstances = true)>]
val mainPort : Operations
val state : State

new(creationPort : DsspServiceCreationPort) = {
inherit DsspServiceBase(creationPort) ;
state = new State()
mainPort = new Operations() }

override this.Start() =
base.Start()

[<ServiceHandler(ServiceHandlerBehavior.Concurrent)>]
member this.GetHandler(get : Get) =
enumerate
(seq {
yield (handler (fun () -> get.ResponsePort.Post(this.state)))
})
end


0 comments: