SharpBoomerang


Channels and Isomorphisms

isomorphism n. Some crazy Greek mathematical thing. See the Wikipedia page for the gory details.

TL;DR it's a function, 'a -> 'b, and its inverse, 'b -> 'a

Isos

In SharpBoomerang, isomorphisms are represented by this type:

1: 
type Iso<'a,'b> = ('a -> 'b) * ('b -> 'a)

Let's take numerical addition, whose inverse is simply subtraction. Here's an isomorphism representing adding one to a number (and subtracting it):

1: 
let add1 = (fun n -> n + 1), (fun n -> n - 1)

See, that's really simple, right? Well sometimes SharpBoomerang can make it even simpler by inferring the inverse function. Thus, we can simplify the definition further and only define the 'a -> 'b function:

1: 
let add1_2 = Iso.ofFn (fun n -> n + 1)

This feature can be very useful when maintaining both the function and its inverse is especially tedious or error-prone. A good example of this is a mapping function:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let numbers = Iso.ofFn (function
                        | 0 -> "zero"
                        | 1 -> "one"
                        | 2 -> "two"
                        | 3 -> "three"
                        | 4 -> "four"
                        | 5 -> "five"
                        | _ -> failwith "Too big")

(snd numbers) "three" // -> 3

Channels

By themselves, isos aren't that useful, but they become very powerful when you combine them with channels. Here's a more complex example to whet our appetite:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let raw = Channel.pipeWith "abc" // create a channel that initially contains "abc"
let map = raw |> Channel.map(Iso.ofFn (fun str -> str.ToUpperInvariant()))

raw.Read(printfn "%A") // prints "abc"
map.Read(printfn "%A") // prints "ABC"

map.Write("DEF")

raw.Read(printfn "%A") // prints "def"
map.Read(printfn "%A") // prints "DEF"

Channels and isos are a couple of the powerful tools SharpBoomerang uses to achieve its bidirectionality.

namespace System
namespace SharpBoomerang
type Iso<'a,'b> = ('a -> 'b) * ('b -> 'a)

Full name: ChannelsAndIsos.Iso<_,_>
val add1 : (int -> int) * (int -> int)

Full name: ChannelsAndIsos.add1
val n : int
val add1_2 : Iso<int,int>

Full name: ChannelsAndIsos.add1_2
Multiple items
type Iso =
  private new : unit -> Iso
  static member ofFn : expr:Expr<('a -> 'b)> -> Iso<'a,'b>
  static member oneWay : fn:('a -> 'b) -> Iso<'a,'b>

Full name: SharpBoomerang.Iso

--------------------
type Iso<'a,'b> = ('a -> 'b) * ('b -> 'a)

Full name: SharpBoomerang.Iso<_,_>


 Total, one-to-one isomorphism of a <> b
static member Iso.ofFn : expr:Quotations.Expr<('a -> 'b)> -> Iso<'a,'b>


 Given a simple function, `'a -> 'b`, attempts to derive the inverse implicitly
val numbers : Iso<int,string>

Full name: ChannelsAndIsos.numbers
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val raw : PipeChannel<string>

Full name: ChannelsAndIsos.raw
module Channel

from SharpBoomerang
val pipeWith : value:'a -> PipeChannel<'a>

Full name: SharpBoomerang.Channel.pipeWith


 Creates a PipeChannel with the given initial value.
val map : IChannel<string>

Full name: ChannelsAndIsos.map
val map : f1:('c -> 'd) * f2:('d -> 'c) -> ch:IChannel<'c> -> IChannel<'d>

Full name: SharpBoomerang.Channel.map


 Maps one channel type onto another using an Iso
val str : string
String.ToUpperInvariant() : string
member PipeChannel.Read : ret:('t -> unit) -> unit
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
abstract member IChannel.Read : ('t -> unit) -> unit


 Reads this `IChannel` and calls the passed function with the result.
abstract member IChannel.Write : 't -> unit


 Writes a`'t` into this `IChannel`.
 If this instance represents a read-only channel, then this method must do nothing.
Fork me on GitHub