有人可以用*非常*简单的术语解释反射包 API 吗?
Posted
技术标签:
【中文标题】有人可以用*非常*简单的术语解释反射包 API 吗?【英文标题】:Can some explain the reflection package API in *very* simple terms? 【发布时间】:2021-02-28 09:55:05 【问题描述】:我很难理解描述反射包的文档/示例。我是一名命令式编程老手,但也是一名 Haskell 新手。可以简单介绍一下吗?
包裹:https://hackage.haskell.org/package/reflection
编辑:致结束此问题的人:这是对 Haskell 反射的初学者介绍。下面的答案很好,其他的也很有用,所以请重新打开。
【问题讨论】:
黑线鳕页面链接的教程有帮助吗:schoolofhaskell.com/user/thoughtpolice/using-reflection? 相关:***.com/a/29929718/1364288 ***.com/a/28651952/1364288 请注意,“反射”是一种高级技术,它依赖于 GHC 内部工作特性的当前实现。这不是适合初学者/中级用户的技术。 【参考方案1】:在最简单的用例中,如果您有一些配置信息希望在一组函数中普遍可用:
data Config = Config w :: Int, s :: String
您可以为需要访问配置的函数添加Given Config
约束:
timesW :: (Given Config) => Int -> Int
然后使用值given
来引用当前配置(因此w given
或s given
来引用其字段):
timesW x = w given * x
还有一些其他功能,有些使用配置,有些不使用:
copies :: Int -> String -> String
copies n str = concat (replicate n str)
foo :: (Given Config) => Int -> String
foo n = copies (timesW n) (s given)
然后您可以在 give
的不同配置下运行计算:
main = do
print $ give (Config 5 "x") $ foo 3
print $ give (Config 2 "no") $ foo 4
这类似于:
全局定义given :: Config
,除非你可以在同一个程序的多个配置下运行计算;
将配置作为额外参数传递给每个函数,除非您避免显式接受配置并传递它的麻烦,例如:
timesW cfg x = w cfg * x
foo cfg n = copies (timesW cfg n) (s cfg)
使用 Reader
monad,但您不必将所有内容都提升为笨拙的 monad 或应用程序级语法,例如:
timesW x = (*) <$> asks w <*> pure x
foo n = copies <$> timesW n <*> asks s
完整示例:
-# LANGUAGE FlexibleContexts #-
import Data.Reflection
data Config = Config w :: Int, s :: String
timesW :: (Given Config) => Int -> Int
timesW x = w given * x
copies :: Int -> String -> String
copies n str = concat (replicate n str)
foo :: (Given Config) => Int -> String
foo n = copies (timesW n) (s given)
main = do
print $ give (Config 5 "x") $ foo 3
print $ give (Config 2 "no") $ foo 4
【讨论】:
这很好,感谢您抽出宝贵时间。这真的感觉像是另一种处理隐式参数的方式,对吗?为什么要准确地称之为“反射”?它不符合我之前对那个词的直觉。 如果您查看原始文章 (Kiselyov and Shan, "Functional Pearl: Implicit Configurations"),他们会将其与 6.2 节中的隐式参数进行比较。术语“反射”指的是实现方法:运行时配置值从值级别“反射”到类型级别。通常在编程中,“反射”指的是相反方向的反射,从类型级别到值级别(例如,Java 中的反射),但它是相同的想法。 实际上,从技术上讲,reflection
包提供的reflect
函数接受类型级别的参数并返回其值级别的值,而reify
用于获取值级别的值到类型级别,所以这里“反射”的预期含义实际上可能是通常的类型级别到值级别的方向。
嗯,我不确定原始文章第 6.2 节中的比较是否适用于 reflection
包。特别是,文章使用了ST
-style 幻像参数来允许多个相同类型的Given
s,而不用担心在错误的时间用错了。几个比较点源于此消歧功能 - 可能全部,我不清楚。
这不是真正应该如何使用Given
和give
。 Given
最好被视为一种处理 singleton 类型的机制,其中类型只有一个值(或者有时存在多个值但您从不关心哪个值)。 Reifies
和 reify
用于配置。以上是关于有人可以用*非常*简单的术语解释反射包 API 吗?的主要内容,如果未能解决你的问题,请参考以下文章