Difference between revisions of "Tangible Value"
m |
m (→Introduction) |
||
Line 12: | Line 12: | ||
== Introduction == |
== Introduction == |
||
+ | |||
+ | As a first example, here is a tangible reverse function: |
||
+ | |||
+ | <haskell> |
||
+ | reverseT :: CTV (String -> String) |
||
+ | reverseT = tv (oTitle "reverse" defaultOut) reverse |
||
+ | </haskell> |
||
+ | |||
+ | The result of <hask>runUI reverseT</hask> looks like this: |
||
+ | |||
+ | Image: reverseT.png |
||
+ | |||
+ | The tv function combines an interface and a value. In this example, the interface is the default for string functions, wrapped with the title "reverse". |
||
+ | |||
+ | ''Interfaces'' in TV are more than just GUIs. Here we use runIO instead of runUI (showing user input in ''italics''): |
||
+ | |||
+ | <code> |
||
+ | *Examples> runIO reverseT |
||
+ | reverse: ''Hello, reversible world!'' |
||
+ | !dlrow elbisrever ,olleH |
||
+ | *Examples> |
||
+ | <code> |
||
+ | |||
+ | What Ive been calling an "interface" is a value of type <hask>COutput a</hask> for a type <hask>a</hask> (for <hask>reverseT</hask>, <hask>a == String->String</hask>). (The reason for the <hask>C</hask> prefix is explained below.) At the heart of TV is a small algebra for constructing these outputs. Weve already seen one output function, oTitle. Another one is showOut, which is an output for all Show types. For instance, |
||
+ | |||
+ | <haskell> |
||
+ | total :: Show a => COutput a |
||
+ | total = oTitle "total" showOut |
||
+ | </haskell> |
||
+ | |||
+ | Beside outputs, there are also inputs. Just as an output is a way to ''deliver'' a value, an input is a way to ''obtain'' a value. For example, here are two <hask>int</hask> inputs, each specifying an initial value and a value range, and each given a title. |
||
+ | |||
+ | <haskell> |
||
+ | apples, bananas :: CInput Int |
||
+ | apples = iTitle "apples" (intIn 3 (0,10)) |
||
+ | bananas = iTitle "bananas" (intIn 7 (0,10)) |
||
+ | </haskell> |
||
+ | |||
+ | Now for the fun part. Lets combine the <hask>apples</hask> and <hask>bananas</hask> inputs and the <hask>total</hask> output to make a ''function-valued'' output. |
||
+ | |||
+ | <haskell> |
||
+ | shoppingO :: COutput (Int -> Int -> Int) |
||
+ | shoppingO = oTitle "shopping list" $ |
||
+ | oLambda apples (oLambda bananas total) |
||
+ | </haskell> |
||
+ | |||
+ | And a TV: |
||
+ | <haskell> |
||
+ | shopping :: CTV (Int -> Int -> Int) |
||
+ | shopping = tv shoppingO (+) |
||
+ | </haskell> |
||
+ | |||
+ | The result of <hask>runUI shopping</hask>: |
||
+ | |||
+ | Image:shopping |
||
+ | |||
+ | And of <hask>runIO shopping</hask>: |
||
+ | <haskell> |
||
+ | *Examples> runIO shopping |
||
+ | shopping list: apples: ''8'' |
||
+ | bananas: ''5'' |
||
+ | total: 13 |
||
+ | </haskell> |
||
+ | |||
+ | Heres an uncurried variation: |
||
+ | |||
+ | <haskell> |
||
+ | shoppingPr :: CTV ((Int,Int) -> Int) |
||
+ | shoppingPr = tv ( oTitle "shopping list" $ |
||
+ | oLambda (iPair apples bananas) total ) |
||
+ | (uncurry (+)) |
||
+ | </haskell> |
||
+ | |||
+ | The runIO presentation is identical to that of <hask>shopping</hask>. The runUI presentation: |
||
+ | |||
+ | Image:shoppingP |
||
+ | |||
+ | |||
+ | TVs, outputs and inputs are not restricted to GUIs and IO. In general, theyre parameterized by an arrow. |
||
+ | |||
+ | <haskell> |
||
+ | data Output (~>) a |
||
+ | data Input (~>) a |
||
+ | type TV (~>) a |
||
+ | </haskell> |
||
+ | |||
+ | In the examples above, weve used two different arrows, namely Phooeys UI arrow (<http://conal.net/Phooey>) and KIO, defined simply as |
||
+ | |||
+ | <haskell> |
||
+ | type KIO = Kleisli IO |
||
+ | </haskell> |
||
+ | |||
+ | Any other monad may be used in place of IO, and other arrows in place of UI and KIO. |
||
+ | |||
+ | The examples <hask>reverseT</hask> and <hask>shoppingT</hask> above used not only the generic Output and Input operations, but also some operations that apply to arrows belonging to the CommonInsOuts class, which includes UI and KIO. The type constructors CInput, COutput, and CTV are universally quantified over CommonInsOuts arrows. |
||
+ | |||
+ | <haskell> |
||
+ | type Common f a = forall (~>). CommonInsOuts (~>) => f (~>) a |
||
+ | </haskell> |
||
+ | |||
+ | <haskell> |
||
+ | type CInput a = Common Input a |
||
+ | type COutput a = Common Output a |
||
+ | type CTV a = Common TV a |
||
+ | </haskell> |
||
+ | |||
+ | Heres a sorting TV: |
||
+ | |||
+ | <haskell> |
||
+ | sortT :: (Read a, Show a, Ord a) => CTV ([a] -> [a]) |
||
+ | sortT = tv (oTitle "sort" $ interactRSOut []) sort |
||
+ | </haskell> |
||
+ | |||
+ | Since <hask>sortT</hask> is polymorphic in value, you may want to type-annotate its uses, e.g., |
||
+ | |||
+ | <hask> runUI (sortT :: CTV ([String] -> [String]))</hask> |
||
+ | |||
+ | Otherwise, <hask>a</hask> will default to <hask>Int</hask>. |
||
+ | |||
+ | With runUI: |
||
+ | |||
+ | Image:sortT |
||
+ | |||
+ | |||
+ | So far, we done a little composition of interfaces and combined them with values to construct TVs. Now lets look at composition of TVs. |
||
+ | |||
+ | First, wrap up the words and unwords functions: |
||
+ | |||
+ | <haskell> |
||
+ | wordsT :: CTV (String -> [String]) |
||
+ | wordsT = tv ( oTitle "function: words" $ |
||
+ | oLambda (iTitle "sentence in" defaultIn) |
||
+ | (oTitle "words out" defaultOut)) |
||
+ | words |
||
+ | </haskell> |
||
+ | |||
+ | Image:wordsT |
||
+ | |||
+ | <haskell> |
||
+ | unwordsT :: CTV ([String] -> String) |
||
+ | unwordsT = tv ( oTitle "function: unwords" $ |
||
+ | oLambda (iTitle "words in" defaultIn) |
||
+ | (oTitle "sentence out" defaultOut)) |
||
+ | unwords |
||
+ | </haskell> |
||
+ | |||
+ | Image:unwordsT |
||
+ | |||
+ | Finally, compose <hask>wordsT</hask>, <hask>unwordsT</hask>, and <hask>sortT</hask> |
||
+ | |||
+ | <haskell> |
||
+ | sortWordsT :: CTV (String -> String) |
||
+ | sortWordsT = wordsT ->| sortT ->| unwordsT |
||
+ | </haskell> |
||
+ | |||
+ | With runUI and runIO: |
||
+ | |||
+ | Image:sortWordsT |
||
+ | |||
+ | <haskell> |
||
+ | *Examples> runIO sortWordsT |
||
+ | sentence in: ''The night Max wore his wolf suit'' |
||
+ | sentence out: Max The his night suit wolf wore |
||
+ | </haskell> |
||
+ | |||
+ | The operator "->" is part of a general approach to value composition from "Control.Arrow.DeepArrow". |
||
== Motivation == |
== Motivation == |
Revision as of 01:38, 17 January 2007
Abstract
TV is a library for composing tangible values (TVs), i.e., values that carry along external interfaces. In particular, TVs can be composed to create new TVs, and they can be directly executed with a friendly GUI, or a *nix-like process that reads and writes character streams. Values and interfaces are combined for direct use, and separable for composition.
TV is for creating software that is ready to use and ready to reuse.
Beside this page, here are some ways to explore TV:
- Read the Haddock docs (with source code, additional examples, and Comment/Talk links)
- Get the code repository: darcs get http://darcs.haskell.org/packages/TV
- Or grab a distribution tarball.
- See the README.
Introduction
As a first example, here is a tangible reverse function:
reverseT :: CTV (String -> String)
reverseT = tv (oTitle "reverse" defaultOut) reverse
The result of runUI reverseT
looks like this:
Image: reverseT.png
The tv function combines an interface and a value. In this example, the interface is the default for string functions, wrapped with the title "reverse".
Interfaces in TV are more than just GUIs. Here we use runIO instead of runUI (showing user input in italics):
- Examples> runIO reverseT
reverse: Hello, reversible world!
!dlrow elbisrever ,olleH
- Examples>
What Ive been calling an "interface" is a value of type COutput a
for a type a
(for reverseT
, a == String->String
). (The reason for the C
prefix is explained below.) At the heart of TV is a small algebra for constructing these outputs. Weve already seen one output function, oTitle. Another one is showOut, which is an output for all Show types. For instance,
total :: Show a => COutput a
total = oTitle "total" showOut
Beside outputs, there are also inputs. Just as an output is a way to deliver a value, an input is a way to obtain a value. For example, here are two int
inputs, each specifying an initial value and a value range, and each given a title.
apples, bananas :: CInput Int
apples = iTitle "apples" (intIn 3 (0,10))
bananas = iTitle "bananas" (intIn 7 (0,10))
Now for the fun part. Lets combine the apples
and bananas
inputs and the total
output to make a function-valued output.
shoppingO :: COutput (Int -> Int -> Int)
shoppingO = oTitle "shopping list" $
oLambda apples (oLambda bananas total)
And a TV:
shopping :: CTV (Int -> Int -> Int)
shopping = tv shoppingO (+)
The result of runUI shopping
:
Image:shopping
And of runIO shopping
:
*Examples> runIO shopping
shopping list: apples: ''8''
bananas: ''5''
total: 13
Heres an uncurried variation:
shoppingPr :: CTV ((Int,Int) -> Int)
shoppingPr = tv ( oTitle "shopping list" $
oLambda (iPair apples bananas) total )
(uncurry (+))
The runIO presentation is identical to that of shopping
. The runUI presentation:
Image:shoppingP
TVs, outputs and inputs are not restricted to GUIs and IO. In general, theyre parameterized by an arrow.
data Output (~>) a
data Input (~>) a
type TV (~>) a
In the examples above, weve used two different arrows, namely Phooeys UI arrow (<http://conal.net/Phooey>) and KIO, defined simply as
type KIO = Kleisli IO
Any other monad may be used in place of IO, and other arrows in place of UI and KIO.
The examples reverseT
and shoppingT
above used not only the generic Output and Input operations, but also some operations that apply to arrows belonging to the CommonInsOuts class, which includes UI and KIO. The type constructors CInput, COutput, and CTV are universally quantified over CommonInsOuts arrows.
type Common f a = forall (~>). CommonInsOuts (~>) => f (~>) a
type CInput a = Common Input a
type COutput a = Common Output a
type CTV a = Common TV a
Heres a sorting TV:
sortT :: (Read a, Show a, Ord a) => CTV ([a] -> [a])
sortT = tv (oTitle "sort" $ interactRSOut []) sort
Since sortT
is polymorphic in value, you may want to type-annotate its uses, e.g.,
runUI (sortT :: CTV ([String] -> [String]))
Otherwise, a
will default to Int
.
With runUI:
Image:sortT
So far, we done a little composition of interfaces and combined them with values to construct TVs. Now lets look at composition of TVs.
First, wrap up the words and unwords functions:
wordsT :: CTV (String -> [String])
wordsT = tv ( oTitle "function: words" $
oLambda (iTitle "sentence in" defaultIn)
(oTitle "words out" defaultOut))
words
Image:wordsT
unwordsT :: CTV ([String] -> String)
unwordsT = tv ( oTitle "function: unwords" $
oLambda (iTitle "words in" defaultIn)
(oTitle "sentence out" defaultOut))
unwords
Image:unwordsT
Finally, compose wordsT
, unwordsT
, and sortT
sortWordsT :: CTV (String -> String)
sortWordsT = wordsT ->| sortT ->| unwordsT
With runUI and runIO:
Image:sortWordsT
*Examples> runIO sortWordsT
sentence in: ''The night Max wore his wolf suit''
sentence out: Max The his night suit wolf wore
The operator "->" is part of a general approach to value composition from "Control.Arrow.DeepArrow".
Motivation
Portability
Known Problems
Plans