Haskell 网络编程系列教程

代码是如何变得抽象?

2018-07-31  本文已影响49人  Lupino

从简单的代码开始,慢慢的迭代,经过几次变得抽象复杂起来。
本文将以定时任务系统 worker 的演化过程来说明代码是如何变得抽象。

第一次迭代

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" do
    n <- name
    liftAff $ someFunc1 n
    done

  work 10

第二次迭代

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask f = do
  n <- name
  liftAff $ f n
  done

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2

  work 10

func1func2 是差不多类似的函数所以我们抽象出 affTask

第三次迭代

affTask 目前只能适用 String -> Aff Unit 类型的函数,
但实际上我们会有很多相类似的函数,如 Int -> Aff Unit
我们需要对 affTask 进行改造,让它适用。

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

mkAffTask :: forall a. (String -> Maybe a) -> (a -> Aff Unit) -> JobT Aff Unit
mkAffTask mk f = do
  n <- mk <$> name
  case n of
    Nothing -> done
    Just n0 -> do
      liftAff $ f n0
      done

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = mkAffTask Just

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = mkAffTask fromString

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3

  work 10

mkAffTask 就是用来构造 affTaskaffTaskI 这两个函数。

第四次迭代

如果我们的函数需要在过一段时间在执行一个呢?

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT, schedLater)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

someFunc4 :: String -> Aff (Maybe Int)
someFunc4 _ = pure $ Just 10

someFunc5 :: Int -> Aff (Maybe Int)
someFunc5 _ = pure Nothing

mkAffTask :: forall a b. (b -> JobT Aff Unit) -> (String -> Maybe a) -> (a -> Aff b) -> JobT Aff Unit
mkAffTask finish mk f = do
  n <- mk <$> name
  case n of
    Nothing -> done
    Just n0 -> do
      r <- liftAff $ f n0
      finish r

maybeLater :: Maybe Int -> JobT Aff Unit
maybeLater Nothing = done
maybeLater (Just v) = schedLater v

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = mkAffTask (const done) Just

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = mkAffTask (const done) fromString

affLaterTask :: (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask = mkAffTask maybeLater Just

affLaterTaskI :: (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI = mkAffTask maybeLater fromString

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3
  addFunc "func4" $ affLaterTask someFunc4
  addFunc "func5" $ affLaterTaskI someFunc5

  work 10

第五次迭代

如果我们的数据不是从 name 来获取呢?

module Worker2 where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
import Effect.Aff.Class (liftAff)
import Data.Time.Duration (Milliseconds (..))
import Periodic.Worker (addFunc, done, name, runWorkerT, work, JobT, schedLater, workload)
import Data.Maybe (Maybe (..))
import Data.Int (fromString)

periodicHost :: {port :: Int, host :: String}
periodicHost = {port: 5000, host: "127.0.0.1"}

someFunc1 :: String -> Aff Unit
someFunc1 _ = delay (Milliseconds 1000.0)

someFunc2 :: String -> Aff Unit
someFunc2 _ = delay (Milliseconds 1000.0)

someFunc3 :: Int -> Aff Unit
someFunc3 _ = delay (Milliseconds 1000.0)

someFunc4 :: String -> Aff (Maybe Int)
someFunc4 _ = pure $ Just 10

someFunc5 :: Int -> Aff (Maybe Int)
someFunc5 _ = pure Nothing

mkAffTask :: forall a b. (b -> JobT Aff Unit) -> (String -> Maybe a) -> JobT Aff String -> (a -> Aff b) -> JobT Aff Unit
mkAffTask finish mk get f = do
  n <- mk <$> get
  case n of
    Nothing -> done
    Just n0 -> do
      r <- liftAff $ f n0
      finish r

maybeLater :: Maybe Int -> JobT Aff Unit
maybeLater Nothing = done
maybeLater (Just v) = schedLater v

affTask_ :: JobT Aff String -> (String -> Aff Unit) -> JobT Aff Unit
affTask_ = mkAffTask (const done) Just

affTask :: (String -> Aff Unit) -> JobT Aff Unit
affTask = affTask_ name

affTaskI_ :: JobT Aff String -> (Int -> Aff Unit) -> JobT Aff Unit
affTaskI_ = mkAffTask (const done) fromString

affTaskI :: (Int -> Aff Unit) -> JobT Aff Unit
affTaskI = affTaskI_ name

affLaterTask_ :: JobT Aff String -> (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask_ = mkAffTask maybeLater Just

affLaterTask :: (String -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTask = affLaterTask_ name

affLaterTaskI_ :: JobT Aff String -> (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI_ = mkAffTask maybeLater fromString

affLaterTaskI :: (Int -> Aff (Maybe Int)) -> JobT Aff Unit
affLaterTaskI = affLaterTaskI_ name

main :: Effect Unit
main = launchAff_ $ runWorkerT launchAff_ periodicHost do
  addFunc "func1" $ affTask someFunc1
  addFunc "func2" $ affTask someFunc2
  addFunc "func3" $ affTaskI someFunc3
  addFunc "func4" $ affLaterTask someFunc4
  addFunc "func5" $ affLaterTaskI someFunc5
  addFunc "func6" $ affTask_ workload someFunc1
  addFunc "func7" $ affTaskI_ workload someFunc3
  addFunc "func8" $ affLaterTask_ workload someFunc4
  addFunc "func9" $ affLaterTaskI_ workload someFunc5

  work 10

总结

经过五次迭代我们构造出一个非常复杂的 mkAffTask 函数。

相关资源:

上一篇 下一篇

猜你喜欢

热点阅读