我看不到语言的评估策略与其纯度之间的联系。考虑到推文作者的声誉,我肯定忽略了一些东西。也许有人可以阐明一些观点。
haskell functional-programming language-implementation lazy-evaluation purity
例如,可以使用Yoneda获得循环融合:
newtype Yoneda f a =
Yoneda (forall b. (a -> b) -> f b)
liftYo :: (Functor f) => f a -> Yoneda f a
liftYo x = Yoneda $ \f -> fmap f x
lowerYo :: (Functor f) => Yoneda f a -> f a
lowerYo (Yoneda y) = y id
instance Functor (Yoneda f) where
fmap f (Yoneda y) = Yoneda $ \g -> y (g . f)
loopFusion = lowerYo . fmap f . fmap g …Run Code Online (Sandbox Code Playgroud) 对自然数使用以下 catamorphism 我可以实现各种算术算法而不必处理递归:
cataNat :: b -> (b -> b) -> Natural -> b
cataNat zero succ = go
where
go n = if (n <= 0) then zero else succ (go (n - 1))
fib :: Natural -> Natural
fib = fst . cataNat (0, 1) (\(a, b) -> (b, a + b))
Run Code Online (Sandbox Code Playgroud)
cataNat对我来说看起来类似于原始递归。无论提供zero和 的哪种组合,至少它的每个应用程序似乎都可以终止succ。在每次迭代中,整个问题都被最小/最简单的问题实例分解。因此,即使它在技术上不是原始递归,它似乎也具有同样的表现力。如果这是真的,则意味着 catamorphism 不足以表达一般递归。为此,我们可能需要一个hylomorphism。我的推理是否正确,也就是说,等价性是否适用于任何类型的 catamorphism,而不仅仅是自然数?
我对代数数据类型的经验很少,因为我使用一种没有本地支持的语言。通常可以使用延续传递风格来获得远程相似的体验,但处理 CPS 编码类型不太舒服。
考虑到这一点,为什么像 Parsec 这样的库会使用 CPS?
newtype ParsecT s u m a
= ParsecT {unParser :: forall b .
State s u
-> (a -> State s u -> ParseError -> m b) -- consumed ok
-> (ParseError -> m b) -- consumed err
-> (a -> State s u -> ParseError -> m b) -- empty ok
-> (ParseError -> m b) -- empty err
-> m b
}
Run Code Online (Sandbox Code Playgroud)
一个线索是try解析器,它通过在两种情况下传递空错误延续来排除消耗的错误情况:
try :: …Run Code Online (Sandbox Code Playgroud) haskell functional-programming algebraic-data-types continuation-passing
这是主要的意见是内置的Javascript原型不应该被延长(或以任何方式改变):
Array.prototype.empty = function () { return this.length === 0; } // don't try that
Run Code Online (Sandbox Code Playgroud)
此规则是否也适用于ES2015符号?
const empty = Symbol("empty");
Array.prototype[empty] = function empty() { return this.length === 0; }
Run Code Online (Sandbox Code Playgroud)
由于symbol是string(原始的,不可变的)和object(标识)的混合,因此根据定义可以没有对象属性命名冲突.
正常对象反射不受符号影响:
Object.getOwnPropertyNames(Array.prototype).indexOf("empty"); // -1
Run Code Online (Sandbox Code Playgroud)
但是ES2015的反思Reflect.ownKeys(Array.prototype)是.
所以这个问题主要是关于我们将来如何使用Reflect.ownKeys和Object.getOwnPropertySymbols未来的问题.
我正在使用lodash _.some函数检查数组中的值.但它的情况敏感.在lodash中是否有任何不区分大小写的搜索功能.下面是我的示例数组结构
[
{
"Name": "Division 1",
"ParentName": null
},
{
"Name": "Division 2",
"ParentName": null
}
]
Run Code Online (Sandbox Code Playgroud)
使用lodash我正在检查这样
_.some(divisionList, ['Name', divisionname]);
Run Code Online (Sandbox Code Playgroud) 请注意,即使此问题中的示例是用Javascript编码的,其基本概念在Haskell中也是常见的,而我虽然更喜欢用Javascript表达自己,但我也很欣赏Haskell中的答案。
在Javascript中,我根据单子原理使用CPS处理异步计算。但是,为了简单起见,我将对这个问题使用正常的延续单子。
一旦我的连续作文增长,我就会发现自己处于需要获得这些作文中间结果的情况。由于必须使用Javascript,因此很容易将此类结果存储在变量中,并在以后访问它们。但是由于我们在谈论连续性,因此访问中间结果意味着调用函数并多次访问它们意味着大量的重新评估。
这似乎非常适合记忆。但是,如果那个函数不返回任何东西而只调用其延续(以及顺便说一句),我该如何记住一个函数的返回值(正如我之前提到的那样,我使用异步函数也不会在Javascript事件循环的当前周期中返回任何东西) )。
似乎我必须提取正确的延续。也许可以通过shift/ 分隔定界符来实现reset,但是我不知道如何应用这些组合器。这个问题可能并没有那么难解决,我只是对持续传递风格的魔幻之地感到困惑……所以请纵容我。
这是ContJava语言中没有备忘的简化示例:
const taggedLog = tag => s =>
(console.log(tag, s), s);
const id = x => x;
const Cont = k => ({
runCont: k,
[Symbol.toStringTag]: "Cont"
});
const contAp = tf => tx =>
Cont(k => tf.runCont(f => tx.runCont(x => k(f(x)))));
const contLiftA2 = f => tx => ty =>
contAp(contMap(f) (tx)) (ty);
const contOf = x => Cont(k => k(x));
const contMap = f => …Run Code Online (Sandbox Code Playgroud)javascript continuations haskell functional-programming memoization
这是我的Task实现方式(即一种Promise但符合monad法律且可取消的方式)。它工作坚如磐石:
const Task = k =>
({runTask: (res, rej) => k(res, rej)});
const tAp = tf => tk =>
Task((res, rej) => tf.runTask(f => tk.runTask(x => res(f(x)), rej), rej));
const tOf = x => Task((res, rej) => res(x));
const tMap = f => tk =>
Task((res, rej) => tk.runTask(x => res(f(x)), rej));
const tChain = fm => mx =>
Task((res, rej) => mx.runTask(x => fm(x).runTask(res, rej), rej));
const log = x => console.log(x);
const elog = …Run Code Online (Sandbox Code Playgroud)javascript continuations functional-programming continuation-passing
这是一个核心递归算法,因为在每次迭代中,它调用自己的数据大于之前的数据:
iterate f x = x : iterate f (f x)
Run Code Online (Sandbox Code Playgroud)
它类似于尾递归累加器风格,但它的累加器是隐式的,而不是作为参数传递。如果不是因为懒惰,那将是无限的。那么 codata 只是 WHNF 中值构造函数的结果,有点像(a, thunk)?或者 codata 是范畴论中的一个数学术语,它在编程领域没有有用的表示?
后续问题:值递归只是核心递归的同义词吗?
Scott 编码列表可以定义如下:
newtype List a =
List {
uncons :: forall r. r -> (a -> List a -> r) -> r
}
Run Code Online (Sandbox Code Playgroud)
与 ADT 版本相反的List是类型和数据构造函数。Scott 编码通过模式匹配来确定 ADT,这实质上意味着删除一层构造函数。这是uncons没有隐式参数的完整操作:
uncons :: r -> (a -> List a -> r) -> List a -> r
-- Nil ^ ^^^^^^^^^^^^^^^^^^ Cons
uncons nil cons (List f) = f nil cons
Run Code Online (Sandbox Code Playgroud)
这是完全有道理的。uncons接受一个常数、一个延续和 aList并产生任何值。
然而,数据构造函数的类型对我来说没有多大意义:
List :: (forall r. r -> (a -> List a -> r) …Run Code Online (Sandbox Code Playgroud) haskell functional-programming algebraic-data-types higher-rank-types scott-encoding
haskell ×7
javascript ×4
recursion ×2
arrays ×1
catamorphism ×1
codata ×1
corecursion ×1
ecmascript-6 ×1
fold ×1
functor ×1
lodash ×1
memoization ×1
prototype ×1
purity ×1
symbols ×1