思考在榆树中看到的模式名称以及其他类似案例

时间:2018-01-29 22:11:44

标签: scala haskell f# elm pointfree

我目前是FP的学生。当我查看不同函数语言的不同语法提供时,我在Elm示例代码中遇到了一个模式。我很好奇。

以下是示例代码

myList = [{foo = "bar1"},{foo = "bar2"}]    
foos = myList |> List.map .foo

在这里的最后一行,List.map正在传递.foo。我相信这种风格被称为无点,但是将属性传递给List.map函数的具体模式呢?

这是更常见的事吗?是否可以在Haskell中执行此操作? F#? Scala呢?谢谢你的帮助。

这里的模式的正式(或非正式?)名称是什么?对象的属性用作获取对象并在其上调用所述属性的函数的简写?

2 个答案:

答案 0 :(得分:8)

如果您将列表视为“数据集”或“表”,并将列表中的每个元素视为“行”,并将元素的数据类型定义为“属性”的枚举,然后你得到的是关系代数意义上的一种“投射”https://en.wikipedia.org/wiki/Projection_(relational_algebra)

这是一个Scala示例,感觉有点像SQL-ish:

case class Row(id: Int, name: String, surname: String, age: Int)

val data = List(
  Row(0, "Bob", "Smith", 25),
  Row(1, "Charles", "Miller", 35),
  Row(2, "Drew", "Shephard", 45),
  Row(3, "Evan", "Bishop", 55)
)

val surnames = data map (_.surname)
val ages = data map (_.age)
val selectIdName = data map { row => (row.id, row.name) }

println(surnames)
// List(Smith, Miller, Shephard, Bishop)

println(selectIdName)
// List((0,Bob), (1,Charles), (2,Drew), (3,Evan))

此处,_.fieldNameRow => TypeOfTheField类型的内联函数文字的简短语法。

在Haskell中,它有点微不足道,因为记录数据类型的声明会自动将所有getter函数带入范围:

data Row = Row { id :: Int
               , name :: String
               , surname :: String
               , age :: Int
               } deriving Show

main = let dataset = [ Row 0 "Bob" "Smith" 25
                     , Row 1 "Charles" "Miller" 35
                     , Row 2 "Drew" "Shephard" 45
                     , Row 3 "Evan" "Bishop" 55
                     ]
       in print $ map name dataset
-- prints ["Bob","Charles","Drew","Evan"]

甚至Java也有类似于版本8的东西:

import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;

class JavaProjectionExample {
  private static class Row {
    private final int id;
    private final String name;
    private final String surname;
    private final int age;
    public Row(int id, String name, String surname, int age) {
      super();
      this.id = id;
      this.name = name;
      this.surname = surname;
      this.age = age;
    }
    public int getId() {
      return this.id;
    }
    public String getName() {
      return this.name;
    }
    public String getSurname() {
      return this.surname;
    }
    public int getAge() {
      return this.age;
    }
  }

  public static void main(String[] args) {
    List<Row> data = Arrays.asList(
      new Row(0, "Bob", "Smith", 25),
      new Row(1, "Charles", "Miller", 35),
      new Row(2, "Drew", "Shephard", 45),
      new Row(3, "Evan", "Bishop", 55)
    );

    List<Integer> ids = data.stream().map(Row::getId).collect(toList());
    List<String> names = data.stream().map(Row::getName).collect(toList());

    System.out.println(ids);
    System.out.println(names);
  }
}

此处,Row::getterName是getter方法的特殊语法,它是Function<Row, FieldType>类型的值。

答案 1 :(得分:7)

这实际上不是免费的,而是语法糖和管道前进算子。对于免费点,请参阅this article

这可以用fsharp写成如下:

let foos = myList |> List.map (fun x -> x.foo)

你可以立即看到这相当于

List.map (fun x -> x.foo) myList

因此管道操作员只需翻转参数,便于将操作链接在一起。因此,您将功能和列表传递给地图。而Elm中的语法糖允许您通过写出.foo来跳过函数参数。我认为这个功能非常方便,顺便说一句。

当您避免指定函数的参数时,无点。它是典型的FP,但一旦变得复杂就难以阅读。

一个例子:

let mySum x y = x + y
//val mySum : x:int -> y:int -> int
mySum 4 7 //11

这是免费的:

let mySum2 = (+)
//val mySum2 : (int -> int -> int)
mySum2 4 7 //11