001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang; 012 013import org.eclipse.golo.runtime.InvalidDestructuringException; 014 015/** 016 * Structure having a head and a tail. 017 * <p> 018 * This interface can be used to define any recursive sequence of object, 019 * such as lists or generators. It is well suited to recursive algorithms, and 020 * thus can be seen as an alternative to the {@code Iterable} interface. 021 * <p> 022 * For example, one can compute the size of such an object with: 023 * <pre class="listing"><code class="lang-java" data-lang="java"> 024 * static int size(HeadTail<?> o) { 025 * if (o.isEmpty()) { return 0; } 026 * return 1 + size(o.tail()); 027 * } 028 * </code></pre> 029 * or in Golo: 030 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 031 * function size = |ht| -> match { 032 * when ht: isEmpty() then 0 033 * otherwhise 1 + size(ht: tail()) 034 * } 035 * </code></pre> 036 * <p> 037 * Note that the {@code size} method is not provided since this interface 038 * can be implemented by infinite generators. 039 * <p> 040 * A {@code List} augmentation is provided, as well as corresponding special 041 * methods on arrays ({@code Object[]}/{@code array[]}) 042 * 043 * @param <E> the type of the elements held in this structure. 044 */ 045public interface HeadTail<E> extends Iterable<E> { 046 047 /** 048 * Get the head of the structure. 049 * 050 * @return the first element of the structure, or {@code null} if the structure is empty. 051 */ 052 E head(); 053 054 /** 055 * Get the tail of the structure. 056 * <p> 057 * To be side effect free, this method should return a deep copy of the original structure or an 058 * immutable view on it. 059 * 060 * @return a new {@code HeadTail} containing all the elements but the first, or a empty one 061 * if no elements remains. 062 */ 063 HeadTail<E> tail(); 064 065 /** 066 * Checks if the structure is empty or not. 067 * 068 * @return {@code true} if the structure contains no element, {@code false} otherwise. 069 */ 070 boolean isEmpty(); 071 072 /** 073 * Util method to wrap a {@code HeadTail} instance into an {@code Iterable} 074 * 075 * @param headTail the instance to wrap 076 * @return an iterable on the values contained in the wrapped instance 077 */ 078 static <E> Iterable<E> toIterable(HeadTail<E> headTail) { 079 return () -> new HeadTailIterator<>(headTail); 080 } 081 082 /** 083 * New style destructuring helper. 084 * 085 * <p>If a remainer if included, it will be the tail of the last extracted value (even if skipped). 086 * 087 * @param number number of variable that will be affected. 088 * @param substruct whether the destructuring is complete or should contains a remainer. 089 * @param toSkip a boolean array indicating the elements to skip. 090 * @return an array containing the values to assign. 091 */ 092 default Object[] __$$_destruct(int number, boolean substruct, Object[] toSkip) { 093 Object[] destruct = new Object[number]; 094 HeadTail<E> current = this; 095 for (int i = 0; i < number - 1; i++) { 096 if (current.isEmpty()) { 097 throw InvalidDestructuringException.tooManyValues(number); 098 } 099 if (Boolean.valueOf(false).equals(toSkip[i])) { 100 destruct[i] = current.head(); 101 } 102 current = current.tail(); 103 } 104 if (substruct && Boolean.valueOf(false).equals(toSkip[number - 1])) { 105 destruct[number - 1] = current; 106 } else if (current.isEmpty()) { 107 throw InvalidDestructuringException.tooManyValues(number); 108 } else if (!current.tail().isEmpty() && Boolean.valueOf(false).equals(toSkip[number - 1])) { 109 throw InvalidDestructuringException.notEnoughValues(number, substruct); 110 } else if (Boolean.valueOf(false).equals(toSkip[number - 1])) { 111 destruct[number - 1] = current.head(); 112 } 113 return destruct; 114 } 115}