2017-06-22 33 views
0

我们正在将我们的强制性大脑转变为一个功能最强大的范例。这个功能给我带来麻烦。我想构建一个数组,其中包含两对或三对,具体取决于条件(是否refreshTokennull)。我怎样才能用FP范式干净地做到这一点?当然,对于命令式的代码和变异,我只是有条件地把.push()的额外值加到看起来很干净的结尾上。如何使用不变性和函数式编程实践构建具有多种可能长度的数组?

这是一个“局部变异是好的”FP预警的例子吗?

(我们在用打字原稿到ReadonlyArray强制更改的,这使得这个有点更难看。)

const itemsToSet = [ 
    [JWT_KEY, jwt], 
    [JWT_EXPIRES_KEY, tokenExpireDate.toString()], 
    [REFRESH_TOKEN_KEY, refreshToken /*could be null*/]] 
    .filter(item => item[1] != null) as ReadonlyArray<ReadonlyArray<string>>; 

AsyncStorage.multiSet(itemsToSet.map(roArray => [...roArray])); 
+2

您的解决方案看起来非常合理,性能可能不是三个元素数组的关注点。我认为这里没有地方突变;你不能引用被突变的原始数组,所以即使发生变异也没有办法观察到变异(编译器可以选择精确地优化初始数组的构造,因为没有可能的方法来引用它) 。即使在Haskell中,运行时也可以在一个地方更新一个值。结论是“突变”=“可观察到的突变”,即“局部突变可以”。 – user2407038

+1

这是JavaScript。只需使用'.push'。 –

回答

5

OP中给出的itemsToSet有什么问题?它对我来说看起来很实用,但可能是因为我缺乏对TypeScript的了解。

在Haskell,没有,但是如果我们使用Maybe第二个元素,我认为itemsToSet可以被翻译成这样:

itemsToSet :: [(String, String)] 
itemsToSet = foldr folder [] values 
    where 
    values = [ 
     (jwt_key, jwt), 
     (jwt_expires_key, tokenExpireDate), 
     (refresh_token_key, refreshToken)] 
    folder (key, Just value) acc = (key, value) : acc 
    folder _ acc = acc 

这里,jwttokenExpireDate,和refreshToken都类型Maybe String

itemsToSet执行右折叠超过values,模式匹配对Just和(隐含地)NothingMaye String元件。如果它是Just值,则它将(key, value)对与累加器acc相比较。如果没有,folder只是返回acc

foldr从右向左遍历values列表,当它访问每个元素时累加累加器。初始累加器值是空列表[]

函数式编程中不需要“局部变异”。一般来说,您可以通过using recursion and introducing an accumulator value从“局部变异”重构为适当的功能风格。

虽然foldr是一个内置函数,但您可以使用递归自己实现它。

+3

'itemsToSet = catMaybes $ fmap sequenceA values';) –

+0

@BenjaminHodgson令人惊叹; 20分钟后,我想我明白了:| –

+2

哈哈!抱歉。不打算在你身上放下脑弹!简单地说:'sequenceA ::(Applicative f,Traversable t)=> t(f a) - > f(t a)'是转置两个构造函数的通用工具。这里'f〜Maybe'和't〜(,)String',所以**'sequenceA ::(String,Maybe String) - > Maybe(String,String)'**。如果我们有't〜[]','sequenceA :: Applicative f => [f a] - > f [a]'会对_many_效果进行排序以产生结果列表。这里只有_one_作用:“容器”类型是元组,它“包含”了它的第二个元素。然后'catMaybes'只是过滤掉'Nothing's。希望解释它! :) –

2

在Haskell,我刚刚创建具有三个元素的数组,并根据条件,按原样传递它,或传递两个元素中的一部分。由于懒惰,除非真正需要,否则不会在第三个元素上花费计算工作量。在TypeScript中,即使不需要也可能会计算第三个元素的代价,但也许这并不重要。

或者,如果你不需要的结构是一个实际的数组(String元素,性能可能不是那么关键,Øñ)直接的接入费用是不是问题,如果长度限于三个元素),我会使用一个单链表来代替。用两个元素创建列表,并根据条件添加第三个元素。这是而不是需要任何突变:3元素列表仅包含未更改的2元素列表作为子结构。

+0

两者都很有趣 - 我正在使用滤波片,基本上和OP一样。链表实现是一个有趣的想法,谢谢!我没有意识到在不可变的环境中使用链表的可能性, –

0

根据描述,我不认为阵列是最好的解决方案,因为您事先知道它们包含或者 2个值或3个值,具体取决于某些条件。这样,我就如下问题进行建模:

type alias Pair = (String, String) 

type TokenState 
    = WithoutRefresh (Pair, Pair) 
    | WithRefresh (Pair, Pair, Pair) 

itemsToTokenState: String -> Date -> Maybe String -> TokenState 
itemsToTokenState jwtKey jwtExpiry maybeRefreshToken = 
    case maybeRefreshToken of 
    Some refreshToken -> 
     WithRefresh (("JWT_KEY", jwtKey), ("JWT_EXPIRES_KEY", toString jwtExpiry), ("REFRESH_TOKEN_KEY", refreshToken)) 

    None -> 
     WithoutRefresh (("JWT_KEY", jwtKey), ("JWT_EXPIRES_KEY", toString jwtExpiry)) 

这样你可以更有效地利用类型系统,并且可以通过做一些不是返回元组更符合人体工程学的进一步提高。

+0

感谢您的回应。在这种情况下,我并不是100%肯定它将会无限期地成为未来的2或3个,所以我正在寻求实施更广泛的FP式解决方案。 –

相关问题