Difference between revisions of "Tangible Value"
m (→A variation) |
|||
Line 217: | Line 217: | ||
applesU = iTitle "apples" (islider 3 (0,10)) |
applesU = iTitle "apples" (islider 3 (0,10)) |
||
bananasU = iTitle "bananas" (islider 7 (0,10)) |
bananasU = iTitle "bananas" (islider 7 (0,10)) |
||
⚫ | |||
⚫ | |||
shoppingUO :: Output UI (Int -> Int -> Int) |
shoppingUO :: Output UI (Int -> Int -> Int) |
||
shoppingUO = oTitle "shopping list" $ |
shoppingUO = oTitle "shopping list" $ |
||
oLambda applesU (oLambda bananasU total) |
oLambda applesU (oLambda bananasU total) |
||
⚫ | |||
+ | We can then make curried and uncurried TVs: |
||
⚫ | |||
shoppingU :: TV UI (Int -> Int -> Int) |
shoppingU :: TV UI (Int -> Int -> Int) |
||
shoppingU = tv shoppingUO (+) |
shoppingU = tv shoppingUO (+) |
||
+ | |||
+ | shoppingPrU :: TV UI ((Int,Int) -> Int) |
||
+ | shoppingPrU = uncurryA $$ shoppingU |
||
</haskell> |
</haskell> |
||
Revision as of 06: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, a process that reads and writes character streams, or many other kinds interfaces. 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.
Tangible values
As a first example, here is a tangible reverse function:
reverseT :: CTV (String -> String)
reverseT = tv (oTitle "reverse" defaultOut) reverse
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".
TV "interfaces" are more than just GUIs. Here are two different renderings of reverseT
. (User input is shown underlined in the runIO
version).
Running:
runUI reverseT
runIO reverseT
*Examples> runIO reverseT reverse: Hello, reversible world. .dlrow elbisrever ,olleH *Examples>
Outputs
What I've 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
Inputs and function-valued outputs
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 inputs, each specifying an initial value and a value range, and each given a title.
apples, bananas :: CInput Int
apples = iTitle "apples" defaultIn
bananas = iTitle "bananas" defaultIn
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 (+)
Running:
runUI shopping
runIO shopping
shopping list: apples: 8 bananas: 5 total: 13
A variation
Here is an uncurried variation:
shoppingPr :: CTV ((Int,Int) -> Int)
shoppingPr = tv ( oTitle "shopping list -- uncurried" $
oLambda (iPair apples bananas) total )
(uncurry (+))
However, there's a much more elegant formulation, using the uncurryA
$$
shoppingPr = uncurryA $$ shopping
Running:
runUI shoppingPr
runIO shoppingPr
shopping list: apples: 8 bananas: 5 total: 13
The general story
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, we've used two different arrows, namely Phooey's UI
arrow 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
.
Common Ins and Outs
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
Sorting examples
Here's 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
:
Composition of TVs
So far, we done a little composition of interfaces and combined them with values to construct TVs. Now let's 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
unwordsT :: CTV ([String] -> String)
unwordsT = tv ( oTitle "function: unwords" $
oLambda (iTitle "words in" defaultIn)
(oTitle "sentence out" defaultOut))
unwords
Finally, compose wordsT
, unwordsT
, and sortT
sortWordsT :: CTV (String -> String)
sortWordsT = wordsT ->| sortT ->| unwordsT
Running:
runUI sortWordsT
runIO sortWordsT
*Examples> runIO sortWordsT sentence in: The night Max wore his wolf suit sentence out: Max The his night suit wolf wore
The operator "->|
"
Arrow-specific interfaces
While some interfaces can be implemented for different kinds of interfaces, others are more specialized. Here are inputs for our shopping example above that specifically work with Phooey's UI arrow.
applesU, bananasU :: Input UI Int
applesU = iTitle "apples" (islider 3 (0,10))
bananasU = iTitle "bananas" (islider 7 (0,10))
shoppingUO :: Output UI (Int -> Int -> Int)
shoppingUO = oTitle "shopping list" $
oLambda applesU (oLambda bananasU total)
We can then make curried and uncurried TVs:
shoppingU :: TV UI (Int -> Int -> Int)
shoppingU = tv shoppingUO (+)
shoppingPrU :: TV UI ((Int,Int) -> Int)
shoppingPrU = uncurryA $$ shoppingU
Note: We could define other type classes, besides CommonInsOuts
. For instance, islider
could be made a method of a GuiArrow
class, allowing it to be rendered in different ways with different GUI toolkits or even using HTML and Javascript.