如何深入准确地理解并掌握一门高阶语言的特性

369 阅读5分钟
原文链接: mp.weixin.qq.com

新的编程语言层出不穷,老的快速演进;有的语言追求纯粹,有的以解决工程问题为主;有的语法追求人性化,有的为机器而生;有些语言特性的变化润物无声,有些翻天覆地。要解决上述问题,尝试 回答以下命题:

  • 该特性有哪些特点?

  • 该特性的“母语”是什么?“母语”会成为其他语言实现的参照或模版。通过学习母语加深理解。

  • 该特性有哪些理论基础?理论都很抽象,不易快速掌握,如能了解其核心思想会让对语言特性的理解变得自然顺畅。

  • 该特性在其他语言中是如何实现的?各种原因常常会导致一种特性在不同语言中的实现存在很大的差别,有些语言通过语法糖来实现,有些在编译器、解释器上实现,有些通过SDK来实现。理解这些差异能避免踩到雷,减少不必要的BUG,保证程序的性能和健壮性。

拿Functional Programming来讲。函数式编程(FP,functional programming)作为一种编程范式,其很多特性已经被引入到各种语言中,包括静态和动态类型的语言。例如:C++、Java、Scala,Go以及Javascript、Perl、Python等。Java在1.8版本中支持了FP的很多特性。

FP的特点如下:

  • FP强调如何描述问题,剩下的交给计算机来做;IP强调如何让计算机一步一步地执行输入的指令来解决问题。

  • 函数是“一等公民”。函数像参数一样传入、返回,习惯了命令式编程,需要改变已有的思维习惯。

  • 函数都是无状态的。相同的输入必定产生相同的输出,除了入参,函数的结果不会受Context的影响。

  • 高阶函数。一个或多个函数作为入参,或返回结果是一个函数的函数。

  • 无副作用。函数不会改变Context。

  • 函数组合。函数之间可以组合完成一个功能。

  • 惰性求值。

  • 异常被视作运算结果的一个特殊值进行处理。

FP的“母语”:Haskell。尽管Haskell不是最早出现的FP语言,但它是目前支持FP特性最全面,其他语言实现FP特性的参照语言之一。

FP的理论基础:范畴论(Category theory)。

范畴是比集合更复杂的数学工具,范畴由三部分组成:

  • 一组对象 

  • 一组态射(morphisms)。每个态射会绑定两个对象,假如f是从源对象A到目标对象B的态射,记作:f:A -> B

  • 态射组合。假如h是态射f和g的组合,记作:h = g o f

范畴与集合相比,后者不去研究元素之间的关系,范畴核心就是态射,也就是对象之间的关系,而且对象本身也可以是态射。要搞清楚范畴理论,前提需要熟悉抽象代数,群论等理论,非一早一夕能掌握,理解范畴论的思想,核心的概念也就达到了学习的目的。

事实上,Haskell中的类型和函数构成一个范畴Hask,Haskell中的类型是Hask范畴的对象,Haskell中的函数是Hask的态射。以范畴论中的函子来讲,它对应Haskell中的Functor,其定义如下:

class Functor (f :: * -> *) where  fmap :: (a -> b) -> f a -> f b

class表示Functor是一个type class,可以理解为一组方法的集合;class的实例就是Haskell中的类型,例如:Int, Char,[]。f为Hask中的态射,从一种类型指向另外一种类型。Functor包括一个方法fmap,它输入一个函数:a->b,一个f a,输出f b。范畴论中,Functor对应函子,它是两个范畴之间的态射,即:F:C->D,其中C,D是两个范畴。f是函子中作用于对象上的部分,fmap是是函子中作用于态射上的部分。例如下图中(+1)表示元素+1的函数,即a->b,f为[]类型,这里把整数态射为列表,执行的结果就是原列表元素加1得到一个新列表。

ghci>fmap (+1) [0..9][1,2,3,4,5,6,7,8,9,10]

Haskell快排示例:

--Quick sortquickSort :: (Ord a) => [a] -> [a]quickSort [] = []quickSort (x:xs)=  let smallerSort = quickSort [a | a <- xs, a <= x]      biggerSort = quickSort [a | a <- xs, a > x]  in smallerSort ++ [x] ++ biggerSort

x:xs是待排序的列表,这里选取首元素作为排序的标杆,smallSort标示小于等于x的元素,biggerSort代表大于x的元素,递归下去,最终结果就是smallSort+x+biggerSort的列表。

Java的FP例子:

import java.math.BigDecimal;import java.util.function.*;public class TestFunction {    public static void main(String[] args) {        /**         * test function and functions composition.         */        Function<BigDecimal, Long> multInt1 = TestFunction::multIntMethod;        Function<Long, Long> multInt2 = returnInnerClassWithState(classStateLong);        Function<Integer, Long> multInt = returnLambda();        Function<Long, BigDecimal> multLong = (input) -> BigDecimal.valueOf(input * Integer                .MAX_VALUE);        System.out.println(multLong.compose(multInt).andThen(multInt1).andThen(multInt2).apply                (100));        multLong.compose(multInt).apply(1);        Predicate<? super Number> ps = (num) -> num.intValue() >= 2;        System.out.println("Predicate<? super Number>: " + ps.test(1));        System.out.println("Predicate<? super Number>: " + ps.test(2L));        Predicate<? extends String> pe = (str) -> str.startsWith("a");  //System.out.println("Predicate<? extends Number>: " + pe.test(null));  //compile failed  //System.out.println("Predicate<? extends Number>: " + pe.test("abc");        Function<String, Long> innerFun = (intVar) -> new Long(intVar + 1);        Function<Number, String> outterFun = (longVar) -> Long.toString(longVar.longValue() + 1);        String resStr = outterFun.compose(innerFun).apply("1");        System.out.println("resStr: " + resStr);        TestFunction mainTest = new TestFunction();        Long res = multInt2.compose(mainTest.objectFun()).andThen(mainTest::zeroForEver).apply                ("123");        System.out.println("res:" + res);        BiFunction<Long, Long, Long> sumLong = (f1, f2) -> f1 + f2;        System.out.println(sumLong.apply(4L, 5L));        Consumer<Integer> integerConsumer = (i) -> System.out.println("integerConsumer:" + i);        Consumer<Number> numberConsumer = (num) -> System.out.println("numberConsumer:" + num                .intValue());        Consumer<Object> objConsumer = (obj) -> System.out.println("objConsumer:" + obj);        integerConsumer.andThen(numberConsumer).andThen(objConsumer).accept(222);        Consumer<Integer> integerConsumer2 = (i) -> System.out.println("integerConsumer2:" + i);        integerConsumer.andThen(integerConsumer2).accept(333);        Supplier<? extends Number> supplier = () -> new Integer(1);        System.out.println("Supplier<? extends Number>: " + supplier.get() + ", class: " +                supplier.get().getClass().getName());        Supplier<? super Number> supplier1 = () -> new Long(2);        System.out.println("Supplier<? super Number>: " + supplier1.get() + ", class: " +                supplier1.get().getClass().getName());        supplier = () -> new Long(1);        System.out.println("Supplier<? extends Number>: " + supplier.get() + ", class: " +                supplier.get().getClass().getName());        UnaryOperator<String> unaryOperator = (str) -> str + "tttt";        System.out.println(unaryOperator.apply("abc"));        Function<String, String> biOperator = Function.identity();        System.out.println(biOperator.apply("abc"));        UnaryOperator.identity().apply("abc");        Function.identity().apply("abc");    }    private static Long classStateLong = 2L;    public static Long multIntMethod(BigDecimal input) {        final Integer factor = -1;        Long res = Long.valueOf(input.longValue() * factor * classStateLong);        System.out.println("multIntMethod, input:" + input.longValue() + ";res:" + res);        return res;    }    private static Function<Integer, Long> returnInnerClass() {        return new Function<Integer, Long>() {            @Override            public Long apply(Integer integer) {                return Long.valueOf(integer * -1);            }        };    }    private static Function<Long, Long> returnInnerClassWithState(Long state) {        return new Function<Long, Long>() {            @Override            public Long apply(Long integer) {                return Long.valueOf(integer * -1 * state);            }        };    }    private static Function<Integer, Long> returnLambda() {        return (i) -> {            Long res = Long.valueOf(i * -1);            System.out.println("returnLambda, i:" + i + "; res:" + res);            return res;        };    }    public Function<String, Long> objectFun() {        return new Function<String, Long>() {            @Override            public Long apply(String s) {                return Long.parseLong(s);            }        };    }    public Long zeroForEver(Long input) {        return input * 0;    }}