Personal tools

Tangible Value

From HaskellWiki

Revision as of 16:39, 28 January 2007 by Conal (Talk | contribs)

Jump to: navigation, search

Contents

1 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:

2 Tangible values

2.1 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
ReverseT.png
*Examples> runIO reverseT
reverse: Hello, reversible world.
.dlrow elbisrever ,olleH
*Examples> 
We'll see later that "
runUI
" and "
runIO
" are both type-specialized synonyms for a more general function.

2.2 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

2.3 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.png
shopping list: apples: 8
bananas: 5
total: 13

2.4 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 <div class="inline-code">
uncurryA
and
$$
</div>
from DeepArrow:
shoppingPr = uncurryA $$ shopping

Running:

runUI shoppingPr
runIO shoppingPr
ShoppingPr.png
shopping list: apples: 8
bananas: 5
total: 13

2.5 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
Other monads may be used in place of
IO
, and other arrows in place of
UI
and
KIO
. The functions
runUI
and
runIO
used in examples above are simply type-specialized synonyms for <div class="inline-code">
runTV
</div>
.
runUI :: TV UI a -> IO ()
runUI = runTV
 
runIO :: TV KIO a -> IO ()
runIO = runTV

2.6 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

2.7 Sorting examples

Here's a sorting TV (see <div class="inline-code">
interactRSOut
</div>
), tested with
runUI
:
sortT :: (Read a, Show a, Ord a) => CTV ([a] -> [a])
sortT = tv (oTitle "sort" $ interactRSOut []) sort
SortT.png
Note that
sortT
is polymorphic in value, and the type variable
a
as defaulted to
Int
. You could instead type-annotate its uses, e.g.,
runUI (sortT :: CTV ([String] -> [String]))

2.8 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:
unwordsT :: CTV ([String] -> String) 
unwordsT = tv ( oTitle "function: unwords" $
                oLambda (iTitle "words in"     defaultIn)
                        (oTitle "sentence out" defaultOut))
              unwords
WordsT.png
unwordsT :: CTV ([String] -> String) 
unwordsT = tv ( oTitle "function: unwords" $
                oLambda (iTitle "words in"     defaultIn)
                        (oTitle "sentence out" defaultOut))
              unwords
UnwordsT.png
Finally, compose
wordsT
,
unwordsT
, and
sortT
sortWordsT :: CTV (String -> String)
sortWordsT = wordsT ->| sortT ->| unwordsT

Running:

runUI sortWordsT
runIO sortWordsT
SortWordsT.png
*Examples> runIO sortWordsT
sentence in: The night Max wore his wolf suit
sentence out: Max The his night suit wolf wore
The operator "<div class="inline-code">
->|
</div>"
is part of a general approach to value composition from DeepArrow.

2.9 Arrow-specific interfaces

While some interfaces can be implemented for different kinds of interfaces, others are more specialized.

2.9.1 GUIs

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:

code runUI rendering
tv shoppingUO (+)
ShoppingU.png
uncurryA $$ tv shoppingUO (+)
ShoppingPrU.png
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.

2.9.2 IO

We can use
IO
operations in TV interfaces via the
KIO
arrow (defined as
Kleisli IO
). TV provides a few functions in its <div class="inline-code">
IO
</div> module
, including a close counterpart to the standard
interact
function.
interactOut :: Output KIO (String -> String)
interactOut = oLambda contentsIn stringOut

Assuming we have a file "test.txt" containing some lines of text, we can use it to test string transformations.

testO :: Output KIO (String -> String)
testO = oLambda (fileIn "test.txt") defaultOut
First, here's a handy wrapping pattern (found in TV's <div class="inline-code">
Misc
</div> module
). For instance,
wrapF show read
turns a string function into value function.
wrapF :: (c->d) -> (a->b) -> ((b->c) -> (a->d))
wrapF after before f = after . f . before
Using
wrapF
it's easy to define higher-order functions that apply another function to the lines or on the words of a string.
onLines, onWords :: ([String] -> [String]) -> (String -> String)
onLines = wrapF unlines lines
onWords = wrapF unwords words

And specializations that operate on each line or word:

perLine,perWord :: (String -> String) -> (String -> String)
perLine f = onLines (map f)
perWord f = onWords (map f)

Some examples:

string function
f
runIO (tv test0 f)
id
To see a World in a Grain of Sand
And a Heaven in a Wild Flower, 
Hold Infinity in the palm of your hand
And Eternity in an hour.
    - William Blake
reverse
ekalB mailliW -    
.ruoh na ni ytinretE dnA
dnah ruoy fo mlap eht ni ytinifnI dloH
 ,rewolF dliW a ni nevaeH a dnA
dnaS fo niarG a ni dlroW a ees oT
onLines reverse
    - William Blake
And Eternity in an hour.
Hold Infinity in the palm of your hand
And a Heaven in a Wild Flower, 
To see a World in a Grain of Sand
perLine reverse
dnaS fo niarG a ni dlroW a ees oT
 ,rewolF dliW a ni nevaeH a dnA
dnah ruoy fo mlap eht ni ytinifnI dloH
.ruoh na ni ytinretE dnA
ekalB mailliW -    
perLine (perWord reverse)
oT ees a dlroW ni a niarG fo dnaS
dnA a nevaeH ni a dliW ,rewolF
dloH ytinifnI ni eht mlap fo ruoy dnah
dnA ytinretE ni na .ruoh
- mailliW ekalB

There are more examples in the TV repository.