haskell - Haskell 中的复杂临时多态性
问题描述
我正在尝试使用类型类来模拟临时多态性并解决涉及更高种类类型的通用案例,但到目前为止无法找出正确的解决方案。
我正在寻找的是定义类似于:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
infixl 0 >>>
-- | Type class that allows applying a value of type @fn@ to some @m a@
class Apply m a fn b | a fn -> b where
(>>>) :: m a -> fn -> m b
-- to later use it in following manner:
(Just False) >>> True -- same as True <$ ma
(Just True) >>> id -- same as id <$> ma
Nothing >>> pure Bool -- same as Nothing >>= const $ pure Bool
(Just "foo") >>> (\a -> return a) -- same as (Just "foo") >>= (\a -> return a)
到目前为止,我已经尝试了多种选择,但都没有奏效。一个简单的解决方案显然失败了:
instance (Functor m) => Apply m a b b where
(>>>) m b = b <$ m
instance (Monad m) => Apply m a (m b) b where
(>>>) m mb = m >>= const mb
instance (Functor m) => Apply m a (a -> b) b where
(>>>) m fn = fmap fn m
instance (Monad m, a' ~ a) => Apply m a (a' -> m b) b where
(>>>) m fn = m >>= fn
由于有大量与第一个实例相关的基金冲突(全部),很高兴涵盖所有案例(duh)。
我也想不出一个合适的类型族方法:
class Apply' (fnType :: FnType) m a fn b | a fn -> b where
(>>>) :: m a -> fn -> m b
instance (Functor m) => Apply' Const m a b b where
(>>>) m b = b <$ m
instance (Monad m) => Apply' ConstM m a (m b) b where
(>>>) m mb = m >>= const mb
instance (Functor m, a ~ a') => Apply' Fn m a (a' -> b) b where
(>>>) m mb = m >>= const mb
instance (Functor m, a ~ a') => Apply' Fn m a (a' -> m b) b where
(>>>) m fn = m >>= fn
data FnType = Const | ConstM | Fn | FnM
type family ApplyT a where
ApplyT (m a) = ConstM
ApplyT (a -> m b) = FnM
ApplyT (a -> b) = Fn
ApplyT _ = Const
在这里,我遇到了几乎相同的问题,第一个实例通过fundep 与所有实例发生冲突。
我想要达到的最终结果有点类似于有时在 Scala 中使用的臭名昭著的磁铁模式。
更新:
为了进一步阐明对这种类型类的需求,这里有一个简单的例子:
-- | Monad to operate on
data Endpoint m a = Endpoint { runEndpoint :: Maybe (m a) } deriving (Functor, Applicative, Monad)
到目前为止,还没有太大的必要>>>
在适当的位置提及操作符,因为用户可能会使用标准集来<$ | <$> | >>=
代替。(实际上,不确定,>>=
因为没有办法用 来Endpoint
定义Monad
)
现在让它更复杂一点:
infixr 6 :::
-- | Let's introduce HList GADT
data HList xs where
HNil :: HList '[]
(:::) :: a -> HList as -> HList (a ': as)
-- Endpoint where a ~ HList
endpoint :: Endpoint IO (HList '[Bool, Int]) = pure $ True ::: 5 ::: HNil
-- Some random function
fn :: Bool -> Int -> String
fn b i = show b ++ show i
fn <$> endpoint -- doesn't work, as fn is a function of a -> b -> c, not HList -> c
另外,想象一下函数fn
也可能被定义m String
为结果。这就是为什么我正在寻找一种方法来向 API 用户隐藏这种复杂性。
值得一提的是,我已经有一个类型类可以转换a -> b -> c
成HList '[a, b] -> c
解决方案
如果目标是对HList
s 进行抽象,那么就这样做。不要通过在每个参数中引入可能的 monad 包装器来混淆事情,事实证明它确实非常复杂。而是使用所有常用工具在功能级别进行包装和提升。所以:
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
data HList a where
HNil :: HList '[]
(:::) :: x -> HList xs -> HList (x : xs)
class ApplyArgs args i o | args i -> o, args o -> i where
apply :: i -> HList args -> o
instance i ~ o => ApplyArgs '[] i o where
apply i _ = i
instance (x ~ y, ApplyArgs xs i o) => ApplyArgs (x:xs) (y -> i) o where
apply f (x ::: xs) = apply (f x) xs
推荐阅读
- python - 如何编写处理不同类型列表的函数
- c# - 色盲和颜色。VS2019 文本编辑器的哪些元素与 C# 语言相关?
- c# - Fortify - 违反信任边界 - Asp.net(C#)- 字符串
- c# - 时间:2019-05-11 标签:c#ews find an email that only sent without save
- android - PluginApplicationException:无法应用插件类“FlutterPlugin”
- log4j - 在翻转场景中将错误的日期附加到 log4j 中的日志文件
- google-chrome-extension - 适用于 Chrome、Firefox、Safari 标准参考和工具的 Web 扩展(浏览器插件)(2021 年)
- java - 如何在android中将表格共享为格式化字符串?
- c# - SignalR 客户端未在集成测试中接收消息
- excel - 行与列的比较(命名范围)