WxHaskell Daan Leijen. Ok…. Dat lijkt me een goed plan. Denk je dat je zo iets dinsdag af kunt...

Post on 17-Jan-2016

213 views 0 download

Transcript of WxHaskell Daan Leijen. Ok…. Dat lijkt me een goed plan. Denk je dat je zo iets dinsdag af kunt...

wxHaskell

Daan Leijen

Ok….

Dat lijkt me een goed plan. Denk je dat je zo iets dinsdag af kunt hebben en presenteren?

Doaitse

On donderdag, september 18, 2003, at 10:43 AM, Daan Leijen wrote:

Nog even over AFP. De turtle-graphics opdracht is volgensmij goed te doen met wxHaskell -- behalve dan dat hetgeinstalleerd dient te worden. Als je wilt kunnen we misschien wat details afspreken endat ik dan wat help om de opdracht vorm te geven met1) een werkend voorbeeld, en 2) concrete vragen.

Overview

wxHaskell as a concrete example of FFI, phantom types, existential types, combinator design, inheritance simulation, and IO as first-class values.

Homework: implement a "turtle" graphics combinator library.

wxHaskell

wxHaskell is a portable GUI library for Haskell build upon the wxWindows (C++) library.

Two layers: a "core" binding (WXCore) and a haskellized layer (WX).

Hello world

hello :: IO ()hello = do f    <- frame    [text := "Hello!"] quit <- button f [text := "Quit" ,on command := close f] set f [layout := widget quit]

Layout combinators

set f [layout := margin 10 (column 5 [floatCentre (label "Hello") ,floatCentre (widget quit) ] ) ]

Demo

What is needed?

Foreign Function Interface (FFI) Model inheritance Create abstractions:

– Layout combinators– Properties (get/set)

FFI

What are the primitives that you need to interface to the imperative world from Haskell?

4 primitive operations are needed.

1. Call the outside world (foreign import)

2. Be called (foreign export)

3. Use foreign data (CInt, Addr)

4. Export haskell data (StablePtr a)

Examples:

foreign import sin :: Double -> IO Double

foreign import strlen :: Addr -> IO CInt

Phantom types

Make "Addr" typesafe:

type Ptr a = Addr

foreign import strlen :: Ptr Char -> IO Int

Abstraction

foreign import strlen :: Ptr Char -> IO Int

strLen :: String -> IntstrLen s = unsafePerformIO $ withCString s $ \cstr -> strlen cstr

Abstraction

withCString :: String -> (Ptr Char -> IO a) -> IO awithCString s f = do p <- malloc (length s+1) mapM_ (poke p) (zip s [0..]) x <- f p free p return x

wxHaskell

windowSetLabel :: Window a -> String -> IO ()windowSetLabel w txt = withCString txt $ \cstr -> primWindowSetLabel w txt

foreign import "windowSetLabel" primWindowSetLabel :: Window a -> Ptr Char -> IO ()

Inheritance

How to model inheritance?

class Window { void setLabel( const char* txt ); ..};

class Frame : public Window { void maximize( void ); ..};

Simple model.

type Window = Ptr CWindowdata CWindow = CWindow

type Frame = Ptr CFramedata CFrame = CFrame

windowCreate :: IO WindowwindowSetLabel :: Window -> String -> IO ()

frameCreate :: IO FrameframeMaximize :: Frame -> IO ()

Conversions?

windowFromFrame :: Frame -> Window

do f <- frameCreate windowSetLabel (windowFromFrame f)

Encode inheritance in phantom type

type Window a = Ptr (CWindow a)data CWindow a = CWindow

type Frame a = Window (CFrame a)data CFrame a = CFrame

windowCreate :: IO (Window ())windowSetLabel :: Window a -> String -> IO ()

frameCreate :: IO (Frame ())frameMaximize :: Frame a -> IO ()

It works now

do f <- frameCreate windowSetLabel f "Hi"

f :: Frame () == Window (CFrame ()) == Ptr (CWindow (CFrame ()))

windowSetLabel :: Window a -> String -> IO ()

Properties

How can we model the "property" methods?

windowGetLabel :: Window a -> IO StringwindowSetLabel :: Window a -> String -> IO ()

windowGetLayout :: Window a -> IO LayoutwindowSetLayout :: Window a -> Layout -> IO ()

..

Get/Set

We would like to have generic "get" and "set" functions:

get :: w -> Attr -> IO aset :: w -> Attr -> a -> IO ()

Typed get/set

get :: w -> Attr w a -> IO aset :: w -> Attr w a -> a -> IO ()

text :: Attr (Window a) String

Attributes

data Attr w a = Attr (w -> IO a) (w -> a -> IO ())

text :: Attr (Window a) Stringtext = Attr windowGetLabel windowSetLabel

Generic get/set

get :: w -> Attr w a -> IO aget w (Attr getter setter) = getter w

set :: w -> Attr w a -> a -> IO ()set w (Attr getter setter) x = setter w x

Problems

I would like to set many "properties" at once:

set frame text "hi"set frame size (sz 300 300)set frame color blue

== (?)

map (set frame) [text "hi", size (sz 300 300), color blue]

Properties

Properties save a value/attribute pair.

data Prop w = Prop (w -> IO ())

prop :: Attr w a -> a -> Prop wprop (Attr getter setter) x = Prop (\w -> setter w x)

set :: w -> [Prop w] -> IO ()set w props = mapM_ setprop props where setprop (Prop setter) = setter w

Now we can set many properties

set frame [prop text "hi" ,prop color blue ,prop size (sz 300 300) ]

Problem

I want a nicer notation:

prop text "hi" text := "hi"

Existentials

data Prop w = (:=) (Attr w a) a

data Prop w = forall a. (:=) (Attr w a) a

New definition of "set"

set :: w -> [Prop w] -> IO ()set w props = mapM_ setprop props where setprop ((Attr getter setter) := x) = setter w x

Definition of "frame"

frame :: [Prop (Frame ())] -> IO (Frame ())frame props = do f <- frameCreate idAny "" rectNull 0 set f props return f

A combinator language for layout

set f [layout := margin 10 (column 5 [floatCentre (label "Hello") ,floatCentre (widget quit) ] ) ]

Example: C++ vs. Combinators

f <- frame [text "Demo"]

ok <- button f [text "Ok"]can <- button f [text "Cancel"]txt <- textCtrl f AlignLeft [clientSize := sz 100 60]

set f [layout := margin 10 $ column 10 [ fill $ widget txt , hfill $ row 10 [widget ok, widget can] ] ]

Abstract data type

data Layout

windowSetLayout :: Window a -> Layout -> IO ()

do f <- frameCreate idAny "Test" rectNull 0 ok <- buttonCreate f idAny "Bye" rectNull 0 windowSetLayout f (widget ok)

Primitives

widget :: Window a -> Layoutlabel :: String -> Layoutspace :: Size -> Layout

Containers

grid :: Int -> Int -> [[Layout]] -> Layout

grid 5 5 [[label "x", widget xinput] ,[label "y", widget yinput]]

Containers 2

row :: Int -> [Layout] -> Layoutcolumn :: Int -> [Layout] -> Layout

row n xs = grid n 0 [xs]

column n xs = grid 0 n (map (\x -> [x]) xs)

column 5 [grid 5 5 [[label "x", widget xinput] ,[label "y", widget yinput]] row 5 [widget ok, widget cancel]]

Attributes

margin :: Int -> Layout -> Layout

align :: Align -> Layout -> Layoutshaped :: Layout -> Layoutexpand :: Layout -> Layout

Stretch

hstretch, vstretch, stretch :: Layout -> Layout

stretch = hstretch . vstretch

column 5 $[grid 5 5 [[label "x", hstretch $ expand $ widget xinput] ,[label "y", hstretch $ expand $ widget yinput]],stretch $ alignBottomRight $ row 5 [widget ok, widget cancel]]

Many helpers

fill = stretch . expandhfill = hstretch . expand

floatBottomRight = stretch . alignBottomRight

empty = space (sz 0 0)glue = fill emptyhglue = hfill empty

Implementation

data Layout = Widget (Window ()) | Label String | Grid Int Int [[Layout]]

Transformers

data Layout = Widget { options :: Options, ..} | Label { options :: Options, ..} | Grid { options :: Options, ..}

data Options = Options{ stretchH :: Bool , stretchV :: Bool , marginW :: Int , .. }

Generate Layout

widget w = Widget defaultOptions wlabel s = Label defaultOptions s

hstretch layout = updateOptions (\opt -> opt { stretchH = True }) layout

updateOptions f layout = layout{ options = f (options layout) }

Translate Layout

windowSetLayout :: Window a -> Layout -> IO ()windowSetLayout w (Label options s) = do lab <- staticTextCreate s sizer <- windowSizerCreate lab (flags options) windowAddSizer w sizer..

flags options = (if stretchH options then wxHSTRETCH else 0) + (if stretchV options then wxVSTRETCH else 0) + ..

Assignment: Turtle graphics

Design and implement an embedding of the LOGO language in Haskell.

Motivate your design decisions! Deadline: this Sunday.

http://wxhaskell.sourceforge.net/assignment.html daan@cs.uu.nl