有时,当我阅读Scala生态系统中的文章时,我会读到"提升"/"解除"一词.不幸的是,没有解释这究竟意味着什么.我做了一些研究,似乎提升与功能价值或类似的东西有关,但我无法找到一个文本,以初学者友好的方式解释实际提升的内容.
通过Lift框架存在额外的混乱,其名称已经提升,但它无助于回答这个问题.
什么是Scala中的"提升"?
我正在使用各种lambda表达式语法测试性能差异.如果我有一个简单的方法:
public IEnumerable<Item> GetItems(int point)
{
return this.items.Where(i => i.IsApplicableFor(point));
}
Run Code Online (Sandbox Code Playgroud)
然后这里有一些与point参数相关的变量提升,因为它是lambda视角的自由变量.如果我将这种方法称为百万次,那么保持原样或以任何方式改变它以改善其性能会更好吗?
我有哪些选择,哪些选项实际可行?据我所知,我必须摆脱自由变量,因此编译器不必创建闭包类并在每次调用此方法时实例化它.与非闭合版本相比,此实例化通常需要大量时间.
问题是我想提出一些通常会起作用的lambda写作指南,因为看起来每次我写一个受到重创的lambda表达式时我都会浪费一些时间.我必须手动测试它以确保它能够工作,因为我不知道要遵循什么规则.
我还写了一个不同版本的同一个方法,不需要任何变量提升(至少我认为它没有,但你了解这个让我知道是否是这种情况):
public IEnumerable<Item> GetItems(int point)
{
Func<int, Func<Item, bool>> buildPredicate = p => i => i.IsApplicableFor(p);
return this.items.Where(buildPredicate(point));
}
Run Code Online (Sandbox Code Playgroud)
在这里查看Gist.只需创建一个控制台应用程序并将整个代码复制到块Program.cs内的文件中namespace.您将看到第二个示例即使不使用自由变量也要慢得多.
我想构建一些lambda最佳使用指南的原因是我之前遇到过这个问题,令我惊讶的是,当使用谓词构建器 lambda表达式时,结果更快.
现在解释一下.我完全迷失在这里,因为当我知道我的代码中有一些繁重的使用方法时,我可能根本不会使用lambdas.但我想避免这种情况,并深究这一切.
我已经尝试实现一个自定义查找类,其内部工作方式与编译器使用自由变量lambda类似.但是,我没有使用闭包类,而是实现了模拟类似场景的实例成员.这是代码:
private int Point { get; set; }
private bool IsItemValid(Item item)
{
return item.IsApplicableFor(this.Point);
}
public IEnumerable<TItem> GetItems(int point)
{ …Run Code Online (Sandbox Code Playgroud) 在Haskell中,liftM2可以定义为:
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do
x1 <- m1
x2 <- m2
return $ f x1 x2
Run Code Online (Sandbox Code Playgroud)
我想把它翻译成Scala.我的第一次尝试如下:
def liftM2[T1, T2, R, M[_]](f: (T1, T2) => R)(ma: M[T1], mb: M[T2]) : M[R] = for {
a <- ma
b <- mb
} yield f(a, b)
Run Code Online (Sandbox Code Playgroud)
我认为这是最明显的可行方式:"flat flatMap不是类型参数M [T1]的成员".是的,我没有说明M[_]是某种单子.所以接下来我尝试的是定义一些结构类型,如:
type Monad[A] = {
def flatMap[B](f: (A) => …Run Code Online (Sandbox Code Playgroud) 假设我有一个表达式,它只是一个成员访问运算符链:
Expression<Func<Tx, Tbaz>> e = x => x.foo.bar.baz;
Run Code Online (Sandbox Code Playgroud)
您可以将此表达式视为子表达式的组合,每个子表达式包含一个成员访问操作:
Expression<Func<Tx, Tfoo>> e1 = (Tx x) => x.foo;
Expression<Func<Tfoo, Tbar>> e2 = (Tfoo foo) => foo.bar;
Expression<Func<Tbar, Tbaz>> e3 = (Tbar bar) => bar.baz;
Run Code Online (Sandbox Code Playgroud)
我想要做的是分解e成这些组件子表达式,以便我可以单独使用它们.
如果我有表达x => x.foo.bar,我已经知道如何中断x => x.foo.如何拉出其他子表达式foo => foo.bar?
我试图在C#中模拟"提升"成员访问操作符,就像CoffeeScript的存在访问操作符一样?..Eric Lippert表示,类似的运营商被考虑用于C#,但没有预算来实施它.
如果这样的运算符存在于C#中,您可以执行以下操作:
value = target?.foo?.bar?.baz;
Run Code Online (Sandbox Code Playgroud)
如果target.foo.bar.baz链的任何部分结果为null,那么整个事情将评估为null,从而避免NullReferenceException.
我想要一个Lift可以模拟这种事情的扩展方法:
value = target.Lift(x => x.foo.bar.baz); //returns target.foo.bar.baz or null
Run Code Online (Sandbox Code Playgroud)
查看Ramda.js的来源,特别是"升力"功能.
这是给定的例子:
var madd3 = R.lift(R.curry((a, b, c) => a + b + c));
madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
Run Code Online (Sandbox Code Playgroud)
因此结果的第一数目是容易的,a,b,和c,是每个阵列的所有的第一元素.第二个对我来说并不容易理解.参数是每个数组的第二个值(2,2,未定义)还是第一个数组的第二个值以及第二个和第三个数组的第一个值?
即使无视这里发生的事情的顺序,我也没有真正看到它的价值.如果我在没有lift它的情况下执行它,我将最终将数组concat作为字符串.这似乎有点像,flatMap但我似乎无法遵循其背后的逻辑.
我正在尝试构造一个类型的函数:
liftSumthing :: ((a -> m b) -> m b) -> (a -> t m b) -> t m b
Run Code Online (Sandbox Code Playgroud)
tmonad变压器在哪里?具体来说,我有兴趣这样做:
liftSumthingIO :: MonadIO m => ((a -> IO b) -> IO b) -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
我摆弄了一些Haskell巫术库,但无济于事.我如何做到正确,或者在某个我找不到的地方有一个现成的解决方案?
我试图采取例如ExceptT a (StateT A M),对于一些具体类型A和monad M,并将它们包装到我的新自定义monad中.
首先,我确定了StateT A M在其他环境中经常出现,因此我决定这将是最好的单独包装在一个单子M1,然后包装ExceptT a M1成M2.
所需的属性是make M1和的M2实例MonadState和类M(假设它被调用MyMonadClass).也M2应该是一个实例MonadError.
首先我从简单类型的同义词开始:
type MyState = StateT A M
type MyBranch a = ExceptT a MyState
Run Code Online (Sandbox Code Playgroud)
然后我想我会首先勾画实例声明(没有实现实例),这就是我第一次被卡住的地方.instance MonadState A (MyState)似乎不是正确的语法.我以为我必须创建newtype MyState' a = StateT a M然后type MyState = MyState A(不要在没有必要的地方使用语言扩展).
然而,一旦我开始将同义词转换为newtype声明,我开始失去与StateT A M和 …
我现在对monad变形金刚有点困难.我正在定义一些利用变压器的不同的非确定性关系.不幸的是,我无法理解如何从一个有效的模型转换到另一个有效的模型.
假设这些关系是"foo"和"bar".假设"foo"将As和Bs与Cs联系起来; 假设"bar"将Bs和Cs与Ds联系起来.我们将用"foo"来定义"bar".为了使事情变得更有趣,这些关系的计算将以不同的方式失败.(由于条形关系取决于foo关系,因此其失败情况是超集.)因此,我给出以下类型定义:
data FooFailure = FooFailure String
data BarFailure = BarSpecificFailure | BarFooFailure FooFailure
type FooM = ListT (EitherT FooFailure (Reader Context))
type BarM = ListT (EitherT BarFailure (Reader Context))
Run Code Online (Sandbox Code Playgroud)
我希望能够用以下函数签名编写关系:
foo :: A -> B -> FooM C
bar :: B -> C -> BarM D
Run Code Online (Sandbox Code Playgroud)
我的问题是,写"酒吧"的定义的时候,我需要能够从"富"的关系接收错误和正确地表示他们在"酒吧"的空间.所以我可以使用表单的功能
convert :: (e -> e') -> ListT (EitherT e (Reader Context) a
-> ListT (EitherT e' (Reader Context) a
Run Code Online (Sandbox Code Playgroud)
我甚至可以通过运行ListT,在EitherT上映射,然后重新组装ListT来编写那个小野兽(因为它发生m [a]可以转换为ListT ma).但这似乎......凌乱.
有一个很好的理由我不能只运行变压器,在它下面做一些东西,并且通常"把它放回去"; 我跑的变压器可能有效果,我不能神奇地撤消它们.但是有一些方法我可以将一个函数提升到变压器堆栈中,为我做一些工作,所以我不必编写convert上面显示的函数吗?
假设我有这个(可以说是误导的)代码片段:
import System.Environment (getArgs)
import Control.Monad.Except
parseArgs :: ExceptT String IO User
parseArgs =
do
args <- lift getArgs
case safeHead args of
Just admin -> parseUser admin
Nothing -> throwError "No admin specified"
parseUser :: String -> Either String User
-- implementation elided
safeHead :: [a] -> Maybe a
-- implementation elided
main =
do
r <- runExceptT parseArgs
case r of
Left err -> putStrLn $ "ERROR: " ++ err
Right res -> print res
Run Code Online (Sandbox Code Playgroud)
ghc 给我以下错误:
Couldn't …Run Code Online (Sandbox Code Playgroud) 鉴于以下内容:
var average = R.lift(R.divide)(R.sum, R.length)
Run Code Online (Sandbox Code Playgroud)
为什么这会作为一个无点的实现average?我不明白为什么我可以通过R.sum,R.length当它们是函数时,因此,我无法映射提升R.divide函数,R.sum而R.length不像下面的例子:
var sum3 = R.curry(function(a, b, c) {return a + b + c;});
R.lift(sum3)(xs)(ys)(zs)
Run Code Online (Sandbox Code Playgroud)
另外,在上述情况下,在值xs,ys和zs相加在一个非确定性上下文中,在这种情况下,提升函数被应用到的值在给定的计算上下文.
进一步阐述,据我所知,施加解除功能就像使用R.ap连续到每个参数.两行都评估相同的输出:
R.ap(R.ap(R.ap([tern], [1, 2, 3]), [2, 4, 6]), [3, 6, 8])
R.lift(tern)([1, 2, 3], [2, 4, 6], [3, 6, 8])
Run Code Online (Sandbox Code Playgroud)
检查文档说:
"提升"arity> 1的函数,以便它可以"映射"满足FantasyLand Apply规范的列表,函数或其他对象.
这对我来说似乎不是一个非常有用的描述.我正试图建立一个关于使用的直觉lift.我希望有人可以提供.