静的型付けの恩恵を受ける方法

タイプが制限されたライブデータは、州から州へと流れます
タイプが制限されたライブデータは、州から州へと流れます

この記事では、コードを設計するときに静的型システムを最大限に活用する方法について説明します。この記事は言語にとらわれないように努めており(常に機能するとは限りません)、例はJavaであり、実際から引用されています。Idrisのような学術言語では、静的型付けより便利なトリックを実行でき、完全な型推論によって式典のサイズが大幅に削減されますが、職場では他の種類の言語で記述しているため、十分な知識を応用できるようにしたいと考えています。実際には、それが今日の私たちの生活をより良くするので...





記事の筋書きの簡単な説明

, , , , , .





, - ( , ). , , .





, .. , . .





, . , .





, : , ( , , , Java) .





, , , .





:





  1. : , .





  2. , , , .





  3. . — , . — .





  4. , .





  5. , .





  6. : DSL, .





  7. .





, .





, , , . Rust ( non lexical lifetimes: , , , ), . ( ": "). , , , , , , .





, , , .. .





, : , , , . :





, , : , Rust , , .





, , , . . , . , , .





Java:





Optional<? extends CharSequence> x = getContent();
/*
   : 
incompatible types: java.lang.String cannot be converted to
capture#1 of ? extends java.lang.CharSequenc
*/
CharSequence y = x.orElse("");
//       :
// CharSequence y = ( (Optional<CharSequence>) x).orElse("");
      
      



, . , , .





, , .





x



Optional



maybe Java, Rust Scala Option



. C# , , nullable , Optional



. Optional.orElse



, null



, .





? extends CharSequence



, , CharSequence



. Java ""



String



, CharSequence



.





, CharSequence



x



y



, ""



. . Java .





, .. CharSequence



, CharSequence



. Optional



. , .





. , , . C, , , , , .





, C++ Java : , , . , , , .





.





典型的な論理エラー

, . , , . , , .





, , , . , , , , — .





一度に全部欲しい

, : " "? — , , .





: , , . , , : -, , -, , -, , - . , , , .





, , , — , , , .





私たちは主に普遍的なポリモーフィズムに興味を持っています

C Java? , C , ? . C , , . , , . , , , - :





void qsort (
    void* base, 
    size_t num, 
    size_t size, 
    int (*comparator)(const void*, const void*)
);
      
      



. void*



.





, , . , ad-hoc , , C ( , ). - .





Java 3 . : ( ) ( ), ad-hoc: .





, .





— . , . , , : . , Rust , .





, , , .





Rust , : .. ( ) .





Java , , , :





class Builder {
    void addNames(String... names) {addNames(List.of(names))}
    void addNames(Iterable<String> names) {/*...*/}
}
      
      



, , . : , , , .





, - . , -, .





: , , , .





, :





//  run    String  T   T   .
<T> T run(Function<String, T> x) {
    return x.apply("");
}

//  run    String  ,
//          .
void run(Consumer<String> x) {
    run(y-> {
        x.accept(y);
        return null;
        });
}
    
void doWork() {
    run(x-> System.out.println(x));             //  .
    run((String x)-> System.out.println(x));    //    .
}
      
      



— : . Java , run(Function)



run(Consumer)



, , : run(Consumer)



, , .. , , run(Function)



. , , , -, , .





, , .





, - , . , — , — . , , , .





.





, , , . . - . , .





, ClassA



, ClassB



, ClassC



. foo



, bar



, baz



. foo



bar



, baz



. ClassB



foo



baz



, ClassC



baz



, , ClassC.foo()



. : ClassC.foo()



ClassB.foo()



ClassA.bar



ClassA.baz



, ClassC.baz



. , , , .





写真があってもすぐにはわかりませんよね?
, ?

, , , . , .





— , .





, . : , , , API.





List.of



— Java, List



. , . List12



, 2 , , , . , .





— , , , ++ . , . , . , , , , .





, , language agnostic , java- , . , , .









, List<T>



, : X



Y



. , List<X>



, List<Y>



? , , — . , X



Z



, , List<Y>



, List<X>



: Z



, List<Y>



. , java heap pollution. ( , )





, , .





, .





:





  1. — : , . Java .





  2. — , List<Y>



    . . Java List<? extends X>



    .





  3. — , List<Y>



    List<X>



    . . Java List<? super Y>



    .





Rust, , . Java C# . .





, , , Java . , Kotlin . , ( ) Java .





Java. List<? extends X>



- , heap pollution. Java : , , null



. : , null



, , (.. List<? extends X>



X



).





orElse(T default)



: T



? extends CharSequence



null



, T get()



CharSequence



. Java — , , . null



heap pollution.





: List<? super Y>



add(T)



Y



, T get(int)



Object



. , Y



List<Y>



, List<X>



List<Object>



, , get



.





, , . , , .. heap pollution .





, — , .









. , List<Y>



List<? extends X>



, . , Map<K, List<X>>



, Map<K, List<Y>>



, : Map<K, ? extends List<? extends X>>



. , . Map<K, List<? extends X>>



, Map<K, List<Y>>



, Map<K, List<X>>



, .. List<? extends X>



, , .









, . , , var



( ). , .





.









Rust C++ / , . .





Java . , , , , .





C

, , .





, , T



source



, blacklist



. :





<T> int filterCount(Collection<T> source, Set<T> blacklist) {
    if (blacklist.isEmpty()) {
        return source.size();
    }
   return (int) source.stream().filter(x->!blacklist.contains(x)).count();
}
      
      



, blacklist



Set



.





Java, , : Collection



, Set



, List



. Collection



contains



, Set



.





, contains



Set



: O(1) HashSet



O(log n) TreeSet



. - Set



-, , Set



. .





, : . , , Java , .





,

, : - - , . - , .





, : . String->String



, , - : .





: SchemaDiff



String name



Nullable



.





final class SchemaDiff {
    final String name;
    final @Nullable String oldType;
    final @Nullable String newType;

    SchemaDiff(
        String name, 
        @Nullable String oldType, 
        @Nullable String newType
    ) {
        this.name = name;
        this.oldType = oldType;
        this.newType = newType;
    }

    @Override
    public String toString() {
        if (oldType != null && newType != null) {
            return String.format(
                "Column %s changed the type: %s->%s",
                name,
                oldType, 
                newType
            );
        }
        if (oldType == null) {
            return String.format(
                "Column %s with type %s has been added", 
                name, 
                newType
            );
        }
        return String.format(
            "Column %s with type %s has been removed", 
            name, 
            oldType
        );
    }
}
      
      



null



. NPE: Optional



, .. : null



, , , , null



, .





toString



. , , oldType



null



.





, , : RemovedColumn



, AddedColumn



TypeChanged



. SchemaDiff



, .





abstract class SchemaDiff {
    final String name;

    protected SchemaDiff(String name) {
        this.name = name;
    }
}

final class RemovedColumn extends SchemaDiff {
    final String type;

    RemovedColumn(String name, String type) {
        super(name);
        this.type = type;
    }

    @Override
    public String toString() {
        return String.format(
            "Column %s with type %s has been removed", 
            name, 
            type
        );
    }
}

final class AddedColumn extends SchemaDiff {
    final String type;

    AddedColumn(String name, String type) {
        super(name);
        this.type = type;
    }

    @Override
    public String toString() {
        return String.format(
            "Column %s with type %s has been added", 
            name, 
            type
        );
    }
}

final class TypeChanged extends SchemaDiff {
    final String oldType;
    final String newType;

    TypeChanged(String name, String oldType, String newType) {
        super(name);
        this.oldType = oldType;
        this.newType = newType;
    }

    @Override
    public String toString() {
        return String.format(
            "Column's %s type has been changed: %s->%s", 
            name, 
            oldType, 
            newType
        );
    }
}
      
      



, , . toString



.





, , , . . , , — .





, .





どのポリモーフィズムでも、同じ種類の計算グラフを1つのグラフにまとめることができます。

- , :





void process(List<Item> items) {
    if (isLegacy) {
        List<Legacy> docs = items.stream()
            .map(x -> toLegacy(x))
            .collect(toList);
        legacyTable.store(docs);
        logEvent(docs.stream().map(x->x.getId()).collect(toList()));
    } else {
        List<Modern> docs = items.stream()
            .map(x->toModern(x, context))
            .collect(toList);
        modernTable.store(docs);
        logEvent(docs.stream().map(x->x.getId()).collect(toList()));
    }
}
      
      



, Legacy



Modern



. toLegacy



toModern



, . legacyTable



modernTable



, .





- . : , - — .





— — .





, :





<T extends WithId> List<T> store(
    List<Item> items,
    Function<Item, T> mapper,
    Table<T> table
) {
    result = items.map(mapper).collect(toList());
    table.store(result);
    return result;
}
      
      



:





void process(List<Item> items) {
    List<? extends WithId> docs = isLegacy ?
        store(items, x -> toLegacy(x), legacyTable) : 
        store(items, x -> toModern(x, context), modernTable);
    logEvent(docs);
}
      
      



logEvent



, , id



.





, - , .. , .





, , var



Kotlin, C# Java, , .. "" : .





Java:





var list = new ArrayList<>(); //   list    ArrayList<Object>.
list.add(1);
      
      



Rust:





let mut vec = Vec::new();   // vec    Vec<i32>
vec.push(1);
      
      



, , . .





. , , :





HashMap<String, String> map = new HashMap<>();
      
      



:





Map<String, String> map = new HashMap<>();
      
      



: HashMap



TreeMap



- Map



, . , , .





Java 11 , , var



, .





, - :





Long2LongOpenHashMap createMap(long[] keys, long[] values);
      
      



- long->long



- -. , .. , . , .





, , , .





Long2LongMap createMap(long[] keys, long[] values);
      
      



- :





var map = createMap(keys, values);
for(long x: xs) {
    f(map.get(x));
}
      
      



! , , .. API . , — - ( ).





, . , ML- , API . , . — . , — , . -.





, , enum-:





enum FeatureType {
    ELMO,
    SLANG,
    LIFETIME_7D,
}
      
      



. , ELMO



EmbeddingEntry



— float, LIFETIME_7D



FloatEntry



, float



— , 7 , SLANG



BlacklistEntry



— . FeatureEntry



, id



, .





, , API:





<TEntry extends FeatureEntry> Collection<TEntry> find(Collection<Id> ids, FeatureType type);
      
      



id



. .





: TEntry



FeatureType



? , :





enum FeatureType() {
    ELMO(EmbeddingEntry.class),
    SLANG(BlacklistEntry.class),
    LIFETIME_7D(FloatEntry.class),
    ;

    private final Class<? extends FeatureEntry> entryClass;

    public FeatureType(Class<? extends FeatureEntry> entryClass) {
        this.entryClass = entryClass;
    }

    public Class<? extends FeatureEntry> getEntryClass() {
        return entryClass;
    }
}
      
      



, , enum-. , .





, . find



TEntry



, . 3, 56, , .





:





final class FeatureType<TEntry extends FeatureEntry> {
    public static final FeatureType<EmbeddingEntry> ELMO = 
        new FeatureType("elmo", EmbeddingEntry.class);
    public static final FeatureType<BlacklistEntry> SLANG = 
        new FeatureType("slang", BlacklistEntry.class);
    public static final FeatureType<FloatEntry> LIFETIME_7D = 
        new FeatureType("lifetime_7d", FloatEntry.class);

    private final Class<TEntry> entryClass;
    private final String name;
    private FeatureType(String name, Class<TEntry> entryClass) {
        this.name = name;
        this.entryClass = entryClass;
    }

    public String getName() {
        return name;
    }

    public Class<TEntry> getEntryClass() {
        return entryClass;
    }
}
      
      



: , enum. — — API :





<TEntry extends FeatureEntry> Collection<TEntry> find(
   Collection<Id> ids, 
   FeatureType<TEntry> type
);
      
      



API, , - . , , API.





, , - - , , ( Java). .





, " ", , , .





. :





static <TLeft, TRight, TResult> 
BiFunction<Optional<TLeft>, Optional<TRight>, Optional<TResult>> 
lift(BiFunction<TLeft, TRight, TResult> function) {
    return (left, right) -> left.flatMap(
        leftVal -> right.map(rightVal -> function.apply(leftVal, rightVal))
    );
}
      
      



, Optional



.





, FeatureType



, "" FeatureType



FeatureEntry



. , FeatureType



, "" FeatureEntry



.





, , . , , , . , , . , - , . ML .





:





<TEntry extends FeatureEntry> Collection<TEntry> find(
    Collection<Id> ids, 
    FeatureType<TEntry> type, 
    Language language
);

<TEntry extends FeatureEntry> Collection<TEntry> find(
    Collection<Id> ids, 
    FeatureType<TEntry> type, 
    Country country
);
      
      



: , runtime , .





:





class FeatureType<TEntry extends FeatureEntry> {
    public static final ByLanguage<EmbeddingEntry> ELMO =
        new ByLanguage<>("elmo", EmbeddingEntry.class);
    public static final ByCountry<BlacklistEntry> SLANG = 
        new ByCountry<>("slang", BlacklistEntry.class);
    public static final ByCountry<FloatEntry> LIFETIME_7D = 
        new ByCountry<>("lifetime_7d", FloatEntry.class);

    private final Class<TEntry> entryClass;
    private final String name;
    private FeatureType(String name, Class<TEntry> entryClass) {
        this.name = name;
        this.entryClass = entryClass;
    }

    public String getName() {
        return name;
    }

    static final class ByLanguage<TEntry extends FeatureEntry>
        extends FeatureType<TEntry> {...}
    static final class ByCountry<TEntry extends FeatureEntry> 
        extends FeatureType<TEntry> {...}
}
      
      



API :





<TEntry extends FeatureEntry> Collection<TEntry> find(
    Collection<Id> ids, 
    FeatureType.ByLanguage<TEntry> type, 
    Language language
);

<TEntry extends FeatureEntry> Collection<TEntry> find(
    Collection<Id> ids, 
    FeatureType.ByCountry<TEntry> type, 
    Country country
);
      
      



.





. , , , , , , .





, . , enum . , - , . , , . . , , .





-, , . , Java generic enum- , — , . , .





— . . , , , -, .





— , - — . , ? ? ? , , ? .





, -, , .





. , heap dump-, . heap dump- - : , . , heap dump- , , LevelDB, .





, , , . , , , Inspection



, , API Inspection



, : , UI.





— , — . , , .





, : - . , , -.





右下、マネージャーではなく、ショゴス
,

DSL vs composability

, - . , , , Java . , - DSL composability ( ).





DSL : , , API, - DSL. , , - , - DSL. , .. , DSL , .





- , , — . , composability.





, , : CompletableFuture<Optional<T>>



. Java :





  1. OptionalFuture



    .





  2. - FutureUtils



    .





-. composable. , OptionalFuture



CompletableFuture



, Optional



CompletableFuture



, . , - CompletableFuture



, , thenCompose



( ), , .





Java, composable : , . , , . -, vs . , , .





, , , composability , . , Apache Lucene.





Apache Lucene — : Twitter, Elasticsearch. , : , — . — API.





, , , , - - : ( Java), ( , , T[]



int[]



) , ( ).





, , , . , ? , Java " T, T, ", .





Apache Lucene InPlaceMergeSorter, , :





//   :
private BlendedTermQuery(Term[] terms, float[] boosts, TermStates[] contexts) {
    assert terms.length == boosts.length;
    assert terms.length == contexts.length;
    this.terms = terms;
    this.boosts = boosts;
    this.contexts = contexts;
    //  terms, boosts  contexts    

    
    //     :      .
    new InPlaceMergeSorter() {
      //     terms,     .
      @Override
      protected int compare(int i, int j) {
        return terms[i].compareTo(terms[j]);
      }

     //         ,      terms
     @Override
      protected void swap(int i, int j) {
        Term tmpTerm = terms[i];
        terms[i] = terms[j];
        terms[j] = tmpTerm;

        TermStates tmpContext = contexts[i];
        contexts[i] = contexts[j];
        contexts[j] = tmpContext;

        float tmpBoost = boosts[i];
        boosts[i] = boosts[j];
        boosts[j] = tmpBoost;
      }
    //   , ..  : -.
    }.sort(0, terms.length);
  }
      
      



, , , . , .





API , , , - - , . API: , , — .





API , :





Foo(float[] boosts) {
    this.boosts = boosts.clone();

    new InPlaceMergeSorter() {
      @Override
      protected int compare(int i, int j) {
        return Float.compare(boosts[i], boosts[j]);
      }

      @Override
      protected void swap(int i, int j) {
        float tmpBoost = this.boosts[i];
        this.boosts[i] = this.boosts[j];
        this.boosts[j] = tmpBoost;
      }
    }.sort(0, terms.length);
  }
      
      



, , .. boosts



this.boosts



, .





. , , , , .





- , , . , - composability, composability, .





API . .





, , , . , , , . , . : , . , , , .





, , , , . , null



null



. . , , , . , , ? , . : UB , null, , , .





, , . , , 1 , . , -, , , .. , . Java.





P.S.

C. .





公開前に記事をレビューしてくれたこれらの人々に感謝します。





  1. ドミトリー・ユダコフ





  2. ドミトリー・ペトロフ





  3. ニコライ・ミシュク





  4. アナスタシアパブロフスカヤ





  5. ポリーナロマンチェンコ





  6. スヴェトラーナ・イェセンコヴァ





  7. ヤン・コルネフ








All Articles