Search code examples
swiftgenericstail

Generic tail in Swift


tail for array:

private extension Array {
    var tail: Array { get { return Array(dropFirst(self)) } }
}

And here is generic version for Sliceable:

public func tail<S: Sliceable>(sequence: S, initializer: ((S.SubSlice) -> S)) -> S {
    return initializer(dropFirst(sequence))
}
let s = tail("12", {String($0)})

Is it possible to rewrite generic tail without initializer arg? I.e. call initializer via sequence type (S() or something)?


Solution

  • For a sliceable type T, T.SubSlice can be different from T. For example String.SubSlice == String, but Array.SubSlice == ArraySlice<T>.

    You can define a protocol which describes all types which can be created from their own subslices:

    public protocol CreatableFromSubslice : Sliceable {
        init(_ subslice : Self.SubSlice)
    }
    

    Even if most (all?) built-in sliceable types can be created from their own subslice, you still have to tell that the compiler with empty extensions:

    extension String : CreatableFromSubslice { }
    extension Array : CreatableFromSubslice { }
    // ... 
    

    Then tail() can be defined as

    public func tail<S: CreatableFromSubslice>(slice: S) -> S {
        return S(dropFirst(slice))
    }
    

    Example:

    let s = tail("12")
    println(s) // "2"
    
    let a = tail([1, 2, 3])
    println(a) // [2, 3]
    

    For types which are equal to their subslice type you could define

    public func tail<S: Sliceable where S.SubSlice == S >(slice: S) -> S {
        return dropFirst(slice)
    }
    

    This can be applied to String, but not to Array.