Programming components
Joe Armstrong (AL/EAB)
joe.armstrong@REDACTED
Tue Aug 23 11:28:05 CEST 2005
Hello world,
This document contains a few ideas that Uffe and I have been playing with.
Erlang lacks a notion of types and a way of specifying components.
This note describes a systems of dynamic types and type serializations
which seems to us to be easy to understand and implement.
Think of it as a sort of grand union of XML-RPC, and UBF.
This is what XML-RPC, SOAP etc. should have been :-)
Later this year I will release a library which simplifies writing
components - for now I just want to discuss the ideas involved.
Comments?
/Joe
On Types, serializations and components
=======================================
Types
=====
Any programming language must have a notion of variables and types.
When we say
Var = Value
We must know the domain that Value belongs to.
We state the domain of Var with a type declaration:
Type Var = T
Meaning that Var has type T.
Type systems always have three parts:
1) A set of primitive types
2) Glue for making complex types from primitive types
3) Aliases for defining new types
We can make a pretty powerful type system with two primitive types
three types of glue and one way of making new types.
The two primitive types are
int() - meaning an integer
str() - meaning a string
The three types of glue are
{T1, T2, ..., Tn} - tuple glue
[T] - list glue
T1 | T2| ... Tn - choice glue
The way of making new types is:
defType Name = T
Serialization of type instances
===============================
Given the above type system we now ask: How can we serialize instances
of the types - easy - here are two possible serializations of type
instances
First the primitive types
Type Instance Erlang Xml
int() 23 23 <i>23</i>
str() "joe" <<"joe">>A <s>joe</s>
Then the glue
Type Instance Erlang Xml
{int(), str()} {23,"joe"} {23,<<"str">} <t>
<i>23</i>
<s>joe</s>
</t>
[int()] [45,67] [45,67] <l>
<i>23</i>
<i>67</i>
</l>
Note the 1:1 correspondence between the Erlang and XML serializations
Components
==========
What is a component?
Definition: A component is a black box with ports. If you send the port a
well-typed message it will respond with a well-typed reply.
Below I discuss two styles of specify the behavior of components.
One uses a CSP like notation the other an RPC like notation.
CSP Components 1
================
s +------------------+
---| Black box xx |
+------------------+
I define a component as a black box with one of more ports.
All black boxes have at least one port called "s" (standard I/O)
To *specify* the behavior of a black box I can write CSP style equations
like this
-component(ftp).
-export_ports(s).
Type fileName = str().
Start = s ? {"get", fileName()} -> s ! ({"ok", str()} | "enoFile"}) -> Start
| s ? "ls" -> [fileName()];
| s ? {"put", fileName(), str()}.
This tells me all that I need to know about the FTP server!
<< the notation is
Process = Port [?|!] Type -> ... -> Process
P ? T means receive a type T from port P
P ! T means send a message of type T to P
>>
Framing
=======
A serialization needs a "wrapper" to say what it is.
So to use my server with the XML encoding I might make up a message like this
Content-Type: text/myXml
Content-Length: NNNN
[blank line]
<t>
<s>get</s>
<s>some file</s>
</t>
Or
Content-Type: text/erlText
Content-Length: NNNN
[blank line]
{<<"get">>, <<"some file">>}
Or
Content-Type: text/erlBin
Content-Length: NNNN
[blank line]
term-to_binary({<<"get">>, <<"some file">>})
CSP Components 2
================
What happens if my components call modules or other components?
My ftp component will use the file component and the lists module
-component(ftp).
-export_ports(s).
-import_ports(p is (s from file)).
-modules(lists).
Type fileName = str().
Start = s ? {"get", fileName()} ->
p ! {"read", filename()} -> Reply
| s ? "ls" -> [fileName()];
...
Reply -> p ? {"ok", str()} -> ...
lists ! {"member", str(), [str()]} -> Ok
Ok -> lists ? "true" -> ...
This type of specification is precise but rapidly becomes unreadable.
"State less RPC" seems nicer
RPC Components
==============
-component(ftp).
Type fileName = str().
Protocol
{"get", fileName()} => {"ok", str()};
"ls" => [fileName()}
...
End
A => B; means if we send the component ftp a type A it will reply with
a type B. This is a normal RPC.
Reverse RPC's are written:
X <= Y
Events
A => $empty
and Notificataions
$empty => E
(or E <= $empty)
Bind
====
Bind associates an IP and a port with a component
> Bind 222.34.56.78 2345 ftp
Means that Port 2345 on IP 222.34.45.78 obeys the ftp protocol (if it
is running)
To use the server
1) Open a connection to 222.34.45.78 port 2345
2) Send it some correctly typed message, like:
Content-Type: text/myXml
Content-Length: NNNN
[blank line]
<t>
<s>get</s>
<s>some file</s>
</t>
Open questions
==============
1) What should the *semantics* of X => Y be
- immediate reply
- will eventually reply
- will eventually reply or timeout
2) Can we interleave A => B and X <= Y
3) Timeouts
Should we write
A => B within Time
4) Which style of component specification is best
CSP, or RCP?
5) Introspection - yes :-)
Things to think about
=====================
1) Erlang strings.
To obtain a beautiful mapping from abstract types to
Erlang and XML I need to represent strings as <<"string">> and
NOT "string"
The change to the pretty printer that I posted earlier
would encourage this usage.
Note: representing strings as binaries *everywhere* has great advantages
(think about it)
2) Dynamic type checking of these types is trivial - but can a type
checker *prove* that your program follows a given component spec.
All comments welcome
/Joe
More information about the erlang-questions
mailing list