要解决的问题

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

新的编程语言层出不穷,老的快速演进;有的语言追求纯粹,有的以解决工程问题为主;有的语法追求人性化,有的为机器而生;有些语言特性的变化润物无声,有些翻天覆地。现实的复杂性使得没有任何一门语言能够满足所有场合的需求,程序员需要准确、深入,快速地掌握一门语言的特性。

解决方案

回答以下问题:

  • 该特性有哪些特点?
  • 该特性的“母语”是什么?“母语”会成为其他语言实现的参照或模版。通过学习母语加深理解。
  • 该特性有哪些理论基础?理论都很抽象,不易快速掌握,如能了解其核心思想会让对语言特性的理解变得自然顺畅。
  • 该特性在其他语言中是如何实现的?各种原因常常会导致一种特性在不同语言中的实现存在很大的差别,有些语言通过语法糖来实现,有些在编译器、解释器上实现,有些通过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->bf[]类型,这里把整数态射为列表,执行的结果就是原列表元素加1得到一个新列表。

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

Haskell快排示例

--Quick sort
quickSort :: (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

--exec on command shell
--fmap (+1) [0..9]

Java函数式编程示例

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;
    }
}