-- HousePosition :: Text -> Int -> HousePosition -- houseBuilding :: Form -> Text -- houseFloor :: Form -> Int HousePosition <$> houseBuilding <*> houseFloor $ form
functor和Applicative
通过<$>和<*>进行简写,所以先了解函数的functor和applicative实现
functor于Applicative的签名如下
1 2 3 4 5 6
classFunctor f where fmap :: (a -> b) -> f a -> f b classFunctor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
根据以上公式,函数的Functor和Applicative分别是
1 2 3 4 5 6 7 8 9 10 11
-- f1 <$> f2 -- f2结果作为f1的输入,得到f2的输入 -> f1 的结果 classFunctor ((->) r) where fmap :: (a -> b) -> ((->) r) a -> ((->) r) b
-- f1 <*> f2 -- 不考虑f1的第一个参数 -- f2的结果作为f1的第二个参数,得到f2的输入 -> f1的结果 classFunctor ((->) r) => Applicative ((->) r) where pure :: a -> f a (<*>) :: ((->) r) (a -> b) -> ((->) r) a -> ((->) r) b
推导
处理HousePosition houseBuilding的关系
HousePosition: Text -> (Text -> HousePosition )
houseBuilding: Form -> Text
houseBuilding的结果Text可以作为HousePosition的参数Text
所以,将HousePosition函数应用于houseBuilding的结果
得到HousePosition <$> houseBuilding
1 2 3 4 5
Text -> Text -> HousePosition <$> Form -> Text = Form -> (Text -> HousePosition)
处理(HousePosition <$> houseBuilding)与houseFloor的关系
(HousePosition <$> houseBuilding): Form -> Int -> HousePosition
-- 定义一个类型级别的自然数,用来表示栈的大小 dataNat = Z | SNat-- Z表示0,S表示后继
-- 定义固定长度的列表类型 dataVec a n where VNil :: Vec a Z-- 空列表,长度为 0 VCons :: a -> Vec a n -> Vec a (S n) -- 非空列表,长度为 n+1
-- 显示固定长度列表 instanceShow a => Show (Vecan) where show VNil = "[]" show (VCons x xs) = "[" ++ showElem x ++ showRest xs ++ "]" where showElem :: Show a => a -> String showElem = show showRest :: Show a => Vec a n -> String showRest VNil = "" showRest (VCons x xs) = ", " ++ showElem x ++ showRest xs
-- 测试用例 someFunc :: IO () someFunc = do let vec1 :: VecInt (S (SZ)) -- 长度为2的列表 vec1 = VCons1 (VCons2VNil) print vec1 let vec2 :: VecInt (S (S (SZ))) -- 长度为3的列表 vec2 = VCons3 (VCons2 (VCons1VNil)) print vec2
Gloss 是一个用于创建图形和动画的 Haskell 库,特别适合用来制作 2D 图形和动画。它提供了简洁的 API 来进行图形渲染,并且能够轻松地在 Haskell 中进行动画和交互式程序开发。
wsl运行gloss
一开始我使用wsl的archlinux启动gloss项目,但是build失败,报错如下
1 2 3 4 5
Error: [S-7282] Stack failed to execute the build plan. While executing the build plan, Stack encountered the error: [S-7011] While building package GLURaw-2.0.0.5 (scroll up to its section to see the error) using:
processValues :: MaybeString -> MaybeString -> MaybeString -> IO () processValues ma mb mc = do case ma of Nothing -> responseFail "ma 为空" Just a -> case mb of Nothing -> responseFail "mb 为空" Just b -> case mc of Nothing -> responseFail "mc 为空" Just c -> responseSuccess "成功"
模式匹配
将多个Maybe组成元组用于模式匹配
1 2 3 4 5 6 7
processValues :: MaybeString -> MaybeString -> MaybeString -> IO () processValues ma mb mc = case (ma, mb, mc) of (Nothing, _, _) -> responseFail "ma 为空" (_, Nothing, _) -> responseFail "mb 为空" (_, _, Nothing) -> responseFail "mc 为空" (Just a, Just b, Just c) -> responseSuccess "成功"
traverse
1 2 3 4 5 6 7
processValues :: MaybeString -> MaybeString -> MaybeString -> IO () processValues ma mb mc = do let values = [ma, mb, mc] let result = traverse id values -- 遍历每个 Maybe 值,保持原有的结构 case result of Nothing -> responseFail "有空值" Just _ -> responseSuccess "成功"
MaybeT
1 2 3 4 5 6 7 8
processValues :: MaybeString -> MaybeString -> MaybeString -> IO () processValues ma mb mc = do runMaybeT $ do a <- MaybeT (maybe (responseFail "ma为空" >> return Nothing) (return . Just) ma) b <- MaybeT (maybe (responseFail "mb为空" >> return Nothing) (return . Just) mb) c <- MaybeT (maybe (responseFail "mc为空" >> return Nothing) (return . Just) mc) liftIO $ responseSuccess "成功" return ()
typeDb site = ReaderTConnection (HandlerForsite) typeService site = ReaderTServiceContext (HandlerForsite) -- 嵌套 typeDbService site = ReaderTConnection (Servicesite)
runDB :: Db site a -> HandlerForApp a runDB = ...
runDBService :: DbService site a -> Service site a runDBService dbService = do -- 解包最外层,得到Service lift $ runDB $ runInnerService dbService where -- 转换ReaderT的Monad参数,等同下方 -- runInnerService :: ReaderT Connection (Service site) a -> ReaderT Connection (HandlerFor site) a runInnerService :: DbService site a -> Db site a runInnerService dbService = hoistReaderT runService dbService
-- 因为 ReaderT r m a = ReaderT { runReaderT :: r -> m a } -- 所以 f :: r -> ma hoistReaderT :: (m a -> n a) -> ReaderT r m a -> ReaderT r n a hoistReaderT nat (ReaderT f) = ReaderT (nat . f)
typeServeFor site a = (RawUserIdGettersite) => ReaderT (ServiceContext site) (HandlerFor site) a
runServeFor :: ServeFor site a -> (HandlerFor site) a runServeFor service = do let sc = ServiceContext ... runReaderT service sc
serveFor1 :: ServeFor site a serveFor1 = do sc <- ask ....
serveFor2 :: ServeFor site a serveFor1 = do sc <- ask ....
myhandler :: Handler a myhandler = do runServeFor serveFor1 runServeFor serveFor2
提供参数
每个Service都有独立的参数类型,提供的方式又有很多种
方式1:放在ServiceContext
为ServiceContext添加一个参数类型
1 2 3 4 5
dataServiceContext site a = ServiceContext { serviceCtxUserId :: RawUserId site , serviceCtxTime :: LocalTime , serviceData :: a -- 新增参数 }
runServeFor需要添加一个参数
1 2 3 4 5 6 7 8
runServeFor :: form -> ServeFor site a -> (HandlerFor site) a runServeFor form service = do -- 需要提供表单给ServiceContext let sc = ServiceContext { ... serviceData = form } runReaderT service sc
classService param site where typeOutput param action :: param -> ServeFor site (Output param)
newtypeAddUserService = AddUserServiceUserForm newtypeAddUserService = AddGroupServiceGroupForm instanceServiceAddUserServiceAppwhere action :: AddUserService -> ServeFor site () instanceServiceAddGroupServiceAppwhere action :: AddGroupService -> ServeFor site () classServiceRunner site where
runService :: (ServiceRunner site) => Service site -> HandlerFor site result instanceServiceRunnerAppwhere runService service = ServiceContext <$> getRawUserId <*> nowDateTime >>= runReaderT (action service)
使用newtype
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
newtypeService site param result = Service { action :: param -> ServeForsiteresult }
addUserService :: ServiceAppUserForm ()
addGroupService :: ServiceAppGroupForm () classServiceRunner site where
runService :: (Service param result site) => param -> HandlerFor site result instanceServiceRunnerAppwhere runService service = ServiceContext <$> getRawUserId <*> nowDateTime >>= runReaderT (action service)
runServeFor :: ServeFor site param a -> ServiceContext site param -> HandlerFor site a runServeFor (ServeFor service) context = runReaderT service context
在ServeFor中使用HandlerFor需要对HandlerFor提升
1 2 3
instanceMonadHandler (ServeForsiteparam) where typeHandlerSite (ServeForsiteparam) = site liftHandler handler = ServeFor $ lift handler
parseLocalDateSpec :: Spec parseLocalDateSpec = describe "parseLocalDate" $ do it "parses a valid date string" $ do let dateStr = "2024-10-25 225530" let expectedDate = LocalTime (fromGregorian 20241025) (TimeOfDay225530) parseLocalDate dateStr `shouldBe` expectedDate
it "returns Nothing for an invalid date string" $ do let invalidDateStr = "invalid-date" evaluate (parseLocalDate invalidDateStr) `shouldThrow` anyErrorCall