歡迎您光臨本站 註冊首頁

Hamler 0.2 正式釋出,基於 Erlang 與 Haskell 的程式語言

←手機掃碼閱讀     admin @ 2020-08-07 , reply:0

我們在這裡很高興地和大家分享 Hamler 0.2 版本釋出的訊息!

Hamler 是一門構建在 Erlang 虛擬機器(VM)上的 Haskell 風格的強型別(Strongly-typed)程式語言,獨特地結合了編譯時的型別檢查推導,與對執行時高併發和軟實時能力的支援。

HamlerCompilerDesign

Hamler 0.2 現已支援大部分 Erlang 的併發程式設計特性,包括基於 Actor Model 的 Message Passing Concurrency 和 OTP Behaviours。

關於 Actor Model

1974年,卡爾-休伊特教授發表了論文《Actor model of computation》。文中,他闡述了 Actor 作為一個計算實體,它會對收到的訊息作出迴應,並可以併發地進行以下操作:

  • 向其他 Actor 傳送有限數量的訊息
  • 建立有限數量的新 Actor
  • 指定下一個收到的訊息所要使用的行為

隨著多核計算和大規模分散式系統的興起,Actor 模型因其天然的併發性、並行性和分散式變得越來越重要。

Process and Mailbox

Hamler/Erlang 中的 Actor 被定義為一個程序,它的工作方式就像一個 OS 程序。每個程序都有自己的記憶體,由一個 Mailbox、一個 Heap、一個 Stack 和一個包含程序資訊的 Process Control Block(PCB) 組成。

Process

Erlang 中的程序是非常輕量的,我們可以在一個正在執行的 Erlang 虛擬機器上快速建立數百萬個程序。

Message Passing Concurrency

“Message passing concurrency(MPS)是兩個或多個程序之間沒有共享資源情況下的併發,它們透過僅傳遞訊息進行通訊。” Actor Model 就是 MPS 模型的一個實現。

參考資料:

MessagePassingConcurrency

AlanKayOnMessaging

Ping/Pong示例


 import Prelude  
 import Control.Process (selfPid)
 
 go :: Process ()  
 go = do  
 self <- selfPid  
 pid <- spawn loop  
 pid ! (self, :ping)  
 receive  
 :pong -> println "Pong!"  
 pid ! :stop
 
 loop :: Process ()  
 loop =  
 receive  
 (from, :ping) -> do  
 println "Ping!"  
 from ! :pong  
 loop  
 :stop -> return ()  
 

Receive … after示例


 go :: Process ()  
 go = do  
 pid <- spawn recvAfter  
 pid ! :foo
 
 recvAfter :: Process ()  
 recvAfter =  
 receive  
 :bar -> println "recv bar"  
 after  
 1000 -> println "timeout"  
 

Selective Receive 示例


 go :: Process ()  
 go = do  
 pid <- spawn selectiveRecv  
 pid ! :bar  
 pid ! :foo
 
 selectiveRecv :: Process ()  
 selectiveRecv = do  
 receive :foo -> println "foo"  
 receive :bar -> println "bar"  
 

OTP Behaviours

Hamler 採用型別類(TypeClass)實現 OTP Behaviour。

TypeClass 定義了具有類似 operation 的一組型別。在我們的實現中,使用 typeclass 來對不同 OTP Behaviour 的型別進行區分。透過為每個 Behavour 定義一個 typeclass 的方式,我們對這些 Behaviour 做了某種程度上的抽象,並在一定程度上增加了型別約束。

GenServer Behaviour

Generic Server Behaviour 是對 客戶端-伺服器 關係模型中伺服器的抽象。如圖所示,在該模型的伺服器側,所有的通用操作都可以被封裝成為一個模組。與 Erlang 一樣,Hamler 將其封裝為 GenServer 的模組。不同的是在 Hamler 中 GenServer 由型別類進行定義,它所有的回撥函式和引數都必須受到型別約束,它在具備 Erlang 的 gen_server 特性的同時,也保證了型別的安全。以 handleCall 和 handleCast 為例:

參考資料 Erlang gen_server Behaviour

ClientServerMode

GenServer Typeclass


 class GenServer req rep st | req -> rep, rep -> st, st -> req where  
 handleCall :: HandleCall req rep st  
 handleCast :: HandleCast req rep st  
 

A simple Server Example


 module Demo.Server  
 ( start  
 , inc  
 , dec  
 , query  
 ) where
 
 import Prelude  
 import Control.Behaviour.GenServer  
 ( class GenServer  
 , HandleCall  
 , HandleCast  
 , Init  
 , startLinkWith  
 , initOk  
 , call  
 , cast  
 , noReply  
 , reply  
 , shutdown  
 )  
 import System.IO (println)
 
 data Request = Inc | Dec | Query  
 data Reply = QueryResult Integer  
 data State = State Integer
 
 name :: Atom  
 name = :server
 
 start :: Process Pid  
 start = startLinkWith name (init 20)
 
 inc :: Process ()  
 inc = cast name Inc
 
 dec :: Process ()  
 dec = cast name Dec
 
 query :: Process Integer  
 query = do  
 QueryResult i <- call name Query  
 return i
 
 instance GenServer Request Reply State where  
 handleCall = handleCall  
 handleCast = handleCast
 
 init :: Integer -> Init Request State  
 init n = initOk (State n)
 
 handleCall :: HandleCall Request Reply State  
 handleCall Query _from (State i) = do  
 println "Call: Query"  
 reply (QueryResult i) (State i)  
 handleCall _req _from st =  
 shutdown :badRequest st
 
 handleCast :: HandleCast Request Reply State  
 handleCast Inc (State n) = do  
 println "Cast: Inc"  
 noReply $ State (n+1)  
 handleCast Dec (State n) = do  
 println "Cast: Dec"  
 noReply $ State (n-1)  
 handleCast _ st = noReply st  
 

GenStatem Behaviour

GenStatem Behaviour 抽象了對於 事件驅動的有限狀態機(Event-driven Finite State Machine) 中通用的操作。對於該型別的狀態機來說,它以觸發狀態轉換的事件作為輸入,而在狀態轉換過程中執行的動作作為輸出,並得到新的狀態。其模型如下:


 State(S) x Event(E) -> Actions(A), State(S')  
 

與 Erlang 中的實現類似,Hamler 使用 GenStatem 型別類對此狀態機的通用操作進行封裝。在 GenStatem 中僅提供一個事件處理的回撥函式。其宣告如下:


 class GenStatem e s d | e -> s, s -> d, d -> e where  
 handleEvent :: HandleEvent e s d  
 

CodeLock FSM Example


 module Demo.FSM.CodeLock  
 ( name  
 , start  
 , push  
 , stop  
 ) where
 
 import Prelude
 
 import Control.Behaviour.GenStatem  
 ( class GenStatem  
 , Action(..)  
 , EventType(..)  
 , Init  
 , OnEvent  
 , initOk  
 , handleWith  
 , unhandled  
 )  
 import Control.Behaviour.GenStatem as FSM
 
 data Event = Button Integer | Lock  
 data State = Locked | Opened  
 data Data = Data  
 { code :: [Integer]  
 , length :: Integer  
 , buttons :: [Integer]  
 }
 
 instance Eq State where  
 eq Locked Locked = true  
 eq Opened Opened = true  
 eq _ _ = false
 
 instance GenStatem Event State Data where  
 handleEvent = handleWith [(Locked, locked), (Opened, opened)]
 
 name :: Atom  
 name = :code_lock
 
 start :: [Integer] -> Process Pid  
 start code = FSM.startLinkWith name (init code)
 
 push :: Integer -> Process ()  
 push n = FSM.cast name (Button n)
 
 stop :: Process ()  
 stop = FSM.stop name
 
 init :: [Integer] -> Init Event State Data  
 init code = initOk Locked d  
 where d = Data $ { code = reverse code  
 , length = length code  
 , buttons = []  
 }
 
 locked :: OnEvent Event State Data  
 locked Cast (Button n) (Data d) =  
 let buttons = take d.length [n|d.buttons]  
 in if buttons == d.code then  
 let actions = [StateTimeout 1000 Lock] in  
 FSM.nextWith Opened (Data d{buttons = []}) actions  
 else FSM.keep (Data d{buttons = buttons})
 
 locked t e d = unhandled t e Locked d
 
 opened :: OnEvent Event State Data  
 opened Cast (Button _) d = FSM.keep d
 
 opened Timeout Lock d = do  
 println "Timeout Lock"  
 FSM.next Locked d
 
 opened t e d = unhandled t e Opened d  
 

Supervisor Behaviour

Supervisor Behaviour 抽象了程序間容錯的通用操作,它作為一個特殊的程序,以 監督者(Supervisor) 的角色管理其子程序,並在出現異常時重啟相關的子程序,以提高系統的容錯能力。

在 Hamler 中,這類行為被封裝為 Supervisor 的型別類,並提供一個 init 回撥函式來配置監督者的行為和子程序列表。這裡的實現與 Erlang 中的 supervisor 是一致的。

Supervision Tree

監督者可以監控上文提到的 GenServer 或 GenStatem 生成的程序,同樣也可以監控另外一個監督者。這便構成了 監控樹(Supervision Tree)。如下圖所示:

SupervisorTree

其中矩形表示一個監督者,圓表示一個工作者(它可以是一個 GenServer,GenStatem 或其它任意的程序)。當有程序異常退出時,監督者會按回調函式中配置的方式進行重啟,例如:

  • ‘1’ 表示 one_for_one:僅重啟異常退出的子程序。
  • ‘a’ 表示 one_for_all:重啟該監督者下所有的子程序。

參考資料:Supervision Principles ,Erlang Supervisor Behaviour

A Supervisor Example


 module Demo.Sup (start) where
 
 import Prelude
 
 import Demo.Event as Event  
 import Demo.Server as Server  
 import Demo.FSM.PushButton as FSM  
 import Control.Behaviour.Supervisor  
 ( Init  
 , initOk  
 , Strategy(..)  
 , childSpec  
 , startSupWith  
 )
 
 name :: Atom  
 name = :sup
 
 start :: Process Pid  
 start = startSupWith name init
 
 init :: Init  
 init = initOk (OneForOne, 10, 100)  
 [ childSpec "Demo.Event" Event.start  
 , childSpec "Demo.Server" Server.start  
 , childSpec "Demo.Statem" FSM.start  
 ]  
 

歡迎加入 Hamler 程式語言社群

Hamler 函式程式語言從發起即是一個開源專案,專案託管在 GitHub: https://github.com/hamler-lang/ 。 Hamler 目前由 EMQ - 杭州映雲科技有限公司 研發團隊主導開發,計劃在 2020 年底前釋出 0.5 版本用於 EMQ X 6.0 的開發。


[admin ]

來源:OsChina
連結:https://www.oschina.net/news/117759/hamler-0-2-released
Hamler 0.2 正式釋出,基於 Erlang 與 Haskell 的程式語言已經有178次圍觀

http://coctec.com/news/all/show-post-247530.html