Một chƣơng trình Elm có thể coi là một đồ thị tín hiệu, các thao tác trong chƣơng trình chủ yếu là thao tác với tín hiệu. Tín hiệu nhƣ là giá trị của dòng dữ liệu thay đổi theo thời gian. Khi tín hiệu đầu vào tác động vào chƣơng trình nó sẽ đƣợc chuyển đổi, kết hợp và lọc để làm thay đổi giá trị trong luồng dữ liệu. Tất cả các thay đổi sẽ đƣợc lƣu trữ lại với một biến trạng thái là State và sẽ trả lại giá trị cập nhật mới nhất. Mô hình dƣới đây sẽ mô tả quá trình hoạt động của tín hiệu trong một chƣơng trình Elm.
44
Các tín hiệu đầu vào giống nhƣ vị trí con trỏ chuột hoặc các thao tác nhấp phím. Đầu vào sẽ nhận các giá trị từ sự kiện bên ngoài vào, tín hiệu đầu vào của Elm phải có một giá trị mặc định và chúng luôn đƣợc định nghĩa. Các chƣơng trình phụ thuộc vào tín hiệu cũng sẽ đƣợc định nghĩa.
type alias Keys = {x: Int, y: Int}
type alias Input = { space: Bool, enter: Bool,
dir1: Keys, dir2: Keys, delta: Time}
Đoạn mã trên định nghĩa 2 kiểu dữ liệu một là tín hiệu đầu vào Input gồm phím Space, phím Enter, các phím mũi tên (Arrows), các phím (w, a, s, d) và cả tín hiệu thời gian (Time). Kiểu tín hiệu thứ 2 là kiểu Keys thể hiện giá trị nhập vào từ bàn phím đƣợc biểu diễn bởi các giá trị số nguyên.
Tín hiệu là những giá trị truy cập trong một chƣơng trình Elm và khi thao tác với nhiều tín hiệu để trao đổi kết hợp chúng thành một dòng dữ liệu chúng ta cần các hàm thao tác với tín hiệu nhƣ: Hàm chuyển đổi tín hiệu (map), hàm kết hợp nhiều tín hiệu (map2, map3, map4, map5) và hàm lƣu trữ trạng thái của tín hiệu
Input
Mouse.position: Signal (Int, Int)
Transform
map: (a -> b) -> Signal a -> Signal b map2: (a -> b -> c) -> Signal a -> Signal b -> Signal c
Update State
foldp: (a -> b -> c) -> Signal a -> Signal b
45
(foldp, foldl, foldr), ngoài ra còn rất nhiều hàm thao tác với tín hiệu đƣợc chứa trong thƣ viện “Signal.elm”. Dƣới đây là một số hàm cơ bản:
Hàm chuyển đổi
Các tín hiệu cơ bản nhƣ vị trí con trỏ chuột hoặc kích thƣớc một cửa sổ sẽ là không thú vị trừ khi chúng ta có thể chuyển đổi chúng thành một vài giá trị khác bằng việc sử dụng hàm map.
map :: (a -> b) -> Signal a -> Signal b
Hàm map chuyển một hàm sang một tín hiệu. Xét đoạn mã dƣới đây: 1 render (x,y) =
2 collage 600 600 [
3 rotate(degrees(toFloat y))(scale(toFloat x/100) (outlined (solid blue) (rect 50 50))),
4 scale (toFloat x/100) (filled red (circle 20)),
5 toForm (show " Hello")
6 ]
7 main = map render Mouse.position
Nhìn trên đoạn mã trên ta thấy hàm render (x,y) chứa hàmcollage collage : Int -> Int -> [Form] -> Element
Hàm này chứa một tập các đồ họa ở dạng Form do ngƣời dùng tạo ra. Ở đây là một hình vuông viềm mầu xanh và kích thƣớc là 50:
(outlined (solid blue)(rect 50 50))
Một hình tròn mầu đỏ bán kính 20 :
(filled red (circle 20)
Hàmrotatecó chức năng xoay hình vuông theo trục tọa độ y(toFloat y)
rotate : Float -> Form -> Form
Hàm sacle có chức năng kéo dãn kích thƣớc của Form. Ở ví dụ trên là ta kéo dãn hình vuông và hình tròn theo trục tọa độ x.
scale : Float -> Form -> Form
Hàm main là hàm biểu diễn chƣơng trình ra ngoài màn hình. Hàm này sử dụng hàm map chuyển tham số của hàm render (x,y) sang thông số của tín hiệu
46
chuột là Mouse.posiontừ đó dẫn đến các giá trị hình vuông và hình tròn sẽ thay đổi theo tín hiệu chuột.
Xét dòng mã sau biểu diễn cặp tín hiệu đầu vào và trả lại tín hiệu dựa vào tín hiệu thời gian:
Input = Signal.sampleOn delta
(Signal.map2 (,) delta Keyboard.arrows)
Hàm (,) có 2 giá trị và gộp chúng lại với nhau thành một bộ. Hàm Input đầu vào là một tín hiệu có một cặp giá trị. Giống nhƣ tọa độ đƣợc tạo ra bởi tín hiệu chuột.
Hàm sampleOn nhận hai tín hiệu đầu vào và trả lại tín hiệu sau dựa trên tín hiệu trƣớc đó. Với dòng mã trên thì có 2 tín hiệu đầu vào là hàm thời gian delta và tín hiệu đƣợc nhập từ bàn phím là các phím mũi tên. Với hàm sampleOn sẽ trả lại tín hiệu đầu vào thứ 2 là Keyboard.arrows.
sampleOn : Signal a -> Signal b -> Signal b
Hàm kết hợp nhiều tín hiệu
map2 :: (a -> b -> c) -> Signal a -> Signal b -> Signal c Điều này cho phép chúng ta kết hợp 2 hay nhiều tín hiệu. Các hàm hoạt động hầu hết trên các giá trị mới có sẵn. Với điều này chúng ta có thể kết hợp với một số tín hiệu trƣớc đó:
1 render (w,h) (x,y) = container w h middle 2 <|collage 600 600 [
3 rotate(degrees (toFloat y)) (scale (toFloat x/100) (outlined (solid blue) (rect 50 50))) ,
4 scale (toFloat x/100) (filled red (circle 20)) ,
5 toForm (show " Hello")
6 ]
7 main = map2 render Window.dimensions Mouse.position
Xét đoạn mã trên ta ta thấy hàm render sẽ có 2 tham số. Dẫn đến hàm main sẽ phải sử dụng hàm map2. Tham số của hàm render sẽ đƣợc truyền thông qua tín hiệu của cửa sổ windown sau đó sang tín hiệu chuột.
47
Hàm container sẽ chứa một thành phần bên trong nó theo một vị trí đƣợc ấn định. Với đoạn mã trên thành phần phía trong sẽ ở giữa cửa sổ màn hình.
container: Int -> Int -> Position -> Element -> Element
Hàm lưu trữ trạng thái
Để nhớ đƣợc các giá trị trƣớc thì Elm phải có một biến trạng thái chức năng của nó là trả lại giá trị cập nhật mới nhất.
foldp :: (a -> b -> b) -> b -> Signal a -> Signal b
Hàm foldp có nghĩa là lần từ quá khứ (foldp from the past), cũng giống nhƣ fodl cho một danh sách có thể đọc lần từ bên trái (fold from the left).
Xét ví dụ:
add a b = a + b
main = map show (foldp add 0(fps 1))
Trong chƣơng trình trên hàm foldp sẽ thể hiện thời gian của chƣơng trình đƣợc chạy và đƣợc cập nhật là 1 lần trong một giây.
add a b = a + b
main = show (foldl add 0[1,2,3])
Với đoạn mã trên hàm foldl sẽ cho kết quả là 6. Chức năng hàm foldl sẽ cập nhật giá trị của hàmaddmới nhất và trả về giá trị cuối cùng.
Hàm foldp nhận các giá trị từ tín hiệu đầu vào nó kết hợp chúng vào một bộ nhớ. Các đầu ra tín hiệu là giá trị gần nhất hay là mới nhất của bộ nhớ.