Giter VIP home page Giter VIP logo

swift-nonempty's Introduction

Swift 5.7 CI @pointfreeco

This repo contains the full source code for the Point-Free website, a video series exploring advanced programming topics in Swift. The codebase is split into 3 pieces:

  • PointFree: This is the core application, and is responsible for routing requests, loading data and rendering HTML and CSS.
  • Styleguide: This library contains functions and data types for creating a consistent style across the entire website.
  • Server: This is the actual executable server. It uses NIO to handle the low-level server responsibilities, and hands everything else over to the PointFree package.

Point-Free Homepage

Getting Started

Interested in a video tour of the code base?

video poster image

The repo contains an extensive test suite and some playgrounds to explore. To get things running:

  • Open up a terminal window and grab the code:

    git clone https://github.com/pointfreeco/pointfreeco.git
    cd pointfreeco
  • Make sure cmark is installed. You can install it with Homebrew:

    brew install cmark # or your preferred installation method
  • Make sure Postgres is installed and running. It's our database of choice. You can install it with Homebrew:

    brew install postgres # or your preferred installation method
    brew services start postgresql # or your preferred launch method
    make db

    (If you use Postgres.app, EnterpriseDB, or another installation method, please follow some additional instructions in the CPostgreSQL README.)

With the project open in Xcode, you can:

  • Run the server locally
    • Select the Server target
    • Run: Command+R
    • Visit http://localhost:8080
  • Explore our playgrounds
    • Select the PointFree target
    • Build: Command+B
    • Open a playground!

Some fun things to explore

There're a lot of fun things to explore in this repo. For example:

  • We develop web pages in playgrounds for a continuous feedback loop. This is made possible by the fact that the entire server stack is composed of pure functions with side-effects pushed to the boundaries of the application. It allows us to load up any request in isolation, including POST requests, all without ever worrying about doing a side-effect. Server side Swift in a playground

  • We use snapshot testing to capture full data structures in order to verify their correctness. Not only do we do this in the traditional way of taking screenshots of web pages at various break points (e.g. on iPhone and desktop), but we can also snapshot any entire request-to-response lifecycle (e.g. the POST to a signup page does the correct redirect).

β–Ώ Step
  ResponseEnded

β–Ώ Request
  POST http://localhost:8080/launch-signup

  [email protected]

β–Ώ Response
  Status 302 FOUND
  Location: /?success=true

Xcode Color Theme

Like the color theme we use in our episodes? Run make colortheme to install locally!

Related projects

Point-Free uses a bunch of interesting open-source software:

  • πŸ—Ί swift-html: A Swift DSL for type-safe, extensible, and transformable HTML documents.
  • πŸ•Έ swift-web: A collection of types and functions for dealing with common web server concerns, such as HTML render, CSS preprocessing, middleware and more.
  • 🎢 swift-prelude: Offers a standard library for experimental functional programming in Swift.
  • 🏷 swift-tagged: Helps us create strong contracts with our data boundaries, like JSON from GitHub and Stripe, and our PostgreSQL data.
  • πŸ“Έ swift-snapshot-testing: Powers our testing infrastructure by taking snapshots of various data structures to guarantee the correctness of their output. We use this on everything from middleware to ensure requests are correctly transformed into responses, and even entire web pages to make sure the site looks correct at a variety of sizes (e.g. on iPhone and desktop).

Explore more of our open-source on the Point-Free organization.

Learn More

Brandon gave a talk about most of the core ideas that went into this project at Swift Summit 2017.

The two sides of writing testable code

Find this interesting?

Then check out Point-Free!

License

The content of this project itself is licensed under the CC BY-NC-SA 4.0 license, and the underlying source code used to format and display that content is licensed under the MIT license.

swift-nonempty's People

Contributors

buscarini avatar clang13 avatar f-meloni avatar fwcd avatar greg avatar jandamm avatar john-flanagan avatar maximkrouk avatar mbrandonw avatar ohwhen avatar p4checo avatar stephencelis avatar tgrapperon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swift-nonempty's Issues

Convert to array

Hi πŸ‘‹

I'm trying this library out in my codebase. I already have a naive extension on Array -- a computed property called nonEmpty -- which returns self if the array has at least one element, or nil otherwise. Literally just

extension Array {
    var nonEmpty: Array<Element>? {
        guard !self.isEmpty else { return nil }
        return self
    }
}

But I would rather use the NonEmptyArray type in this library. So I changed my extension to return a NonEmptyArray if possible, like this:

import NonEmpty

extension Array {
    var nonEmpty: NonEmptyArray<Element>? {
        guard let head = self.first else { return nil }
        let tail = Array(self.dropFirst())
        return NonEmptyArray(head, tail)
    }
}

Now the problem is that all my existing code that depends on this extension still expects the result to be an Array, and not a NonEmptyArray. The two are of course not interchangeable.

Logically, if you think about it, a NonEmptyArray is functionally equivalent to an Array (though not the other way around). Any operation or method that takes a list of something should also work with a non-empty list of those things. So I was wondering if you have any tips for elegantly converting from NonEmptyArray to Array? Best I've come up with so far is a computed property on NonEmptyArray, or an initialiser on Array. Swift is (for better or for worse) deliberately designed to avoid implicit conversions, making these strategies quite verbose.

If this were a new project, I could just use NonEmptyArray instead of Array anywhere that it makes sense, which I imagine is in most places. But doing that conversion now would be too painful.

Error when decoding an array to a a non empty array

Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "poiList", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

This is my structre

struct LocationCarModel: Codable {
    
    // MARK: - Properties
    var poiList: NonEmptyArray<PointOfInterest>
    
    // MARK: - PointOfInterest
    struct PointOfInterest: Codable {
        var id: Int
        var coordinate: Position
        var fleetType: String
        let numberPlate = "HCD837EC"
        let model: String = "Tesla S"
        let fuel: Double = 0.9
    }
}

This is the response I'm getting https://fake-poi-api.mytaxi.com/?p2Lat=53.394655&p1Lon=9.757589&p1Lat=53.694865&p2Lon=10.099891

and this how I'm decoding it.

 public extension Decodable {
   
   static func parse(from item: Any?, strategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> Self? {
       
       guard let data = self.data(from: item) else {
           return nil
       }
       
       let decoder = JSONDecoder()
       decoder.keyDecodingStrategy = strategy
       
       do {
           let result = try decoder.decode(Self.self, from: data)
           return result
       } catch {
           debugPrint(error)
           return nil
       }
   }
   
   private static func data(from item: Any?) -> Data? {
       switch item {
       case let data as Data:
           return data
       case let string as String:
           return string.data(using: .utf8)
       case .some(let item):
           return try? JSONSerialization.data(withJSONObject: item, options: [])
       case nil:
           return nil
       }
   }
}

and the line to decode using the above function is

let model = LocationCarModel.parse(from: data)

Any ideas how I can solve this issue? Any help would be appreciated. Thank you in advance.

`ExpressibleByArrayLiteral` and `ExpressibleByDictionaryLiteral` conformances

It would be very convenient to have these conformances, since non-empty collections could be initialized as naturally as the built-in collection types:

let x: NonEmpty<[Int]> = [1, 2, 3]
let y: NonEmpty<[Int: String]> = [1: "abc", 2: "def"]

However, since ExpressibleByArrayLiteral and ExpressibleByDictionaryLiteral don't allow their initializers to fail, we would have to crash at runtime, which might not be desirable given the description of the library as a compile-time guarantee:

init(arrayLiteral: Element...) {
  guard !arrayLiteral.isEmpty else { fatalError("Non-empty array literal!") }
  self.init(rawValue: arrayLiteral)!
}

or

init(arrayLiteral: Element...) {
  assert(!arrayLiteral.isEmpty, "Non-empty array literal!")
  self.init(rawValue: arrayLiteral)!
}

Thoughts?

EXC_BAD_ACCESS crash when using SwiftUI's move(fromOffsets:toOffset)

I'm using SwiftUI's move(fromOffsets:toOffsets) method on MutableCollection to move an element in my non-empty array, since it's O(n log n), whereas if I were to do it manually using remove(at:) and insert(at:) the operation would be O(n^2). However, using it on NonEmpty always leads to a crash:

import NonEmpty
import SwiftUI

var nonEmpty: NonEmptyArray<String> = .init("Hello", "Kenobi...", "there!")

nonEmpty.move(fromOffsets: IndexSet(integer: 1), toOffset: 3) // Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f603fc0)

print(nonEmpty)

Using a normal Array works just fine:

var array: Array<String> = ["Hello", "Kenobi...", "there!"]

array.move(fromOffsets: IndexSet(integer: 1), toOffset: 3)

print(array) // ["Hello", "there!", "Kenobi..."]

Could it be due to some quirk in NonEmpty's conformance to MutableCollection?

Internal Codable conformances prevent custom implementations

I was attempting to decode a network response that contains an array directly to NonEmpty. However, the synthesized decodable init does not work with an array as the input. I attempted to override the synthesized init via an extension, but it's ignored at runtime.

I'm wondering if we could remove that conformance from the library and let clients add the conformance as needed to allow for more flexible decoding. The other option is that I'm doing something wrong, of course :)

Range/ClosedRange input support for NonEmpty initializer

It looks like if I were to pass a NonEmptyArray an input of type Range or ClosedRange, NonEmptyArray actually produces an array comprising a single element, which is that range itself, instead of an array comprising the numbers within the range:

// Succeeds
func testArrayRange() {
  let input = 0...2
  let fooList: [String] = Array(input).map {
    "\($0)"
  }
  XCTAssertEqual(fooList, ["0", "1", "2"])
}

// Fails
func testNonEmptyArrayRange() {
  let input = 0...2
  let fooList: [String] = NonEmptyArray(input).map {
    "\($0)"
  }
  XCTAssertEqual(fooList, ["0", "1", "2"])
}

I tried creating a fix myself so I could just create a PR and save you all time, but I'm struggling to get a way to generate the expanded array and map that to an existing NonEmpty convenience initializer. Maybe I'm the only one in the world to try and use NonEmptyArray in this manner and it's not worth the effort ;)

Sendable

If the elements in the collection are Sendable, NonEmpty variant of it should be too

build fails using release config

If building using the release config on the master branch:

swift build -c release

The build fails with a bunch of errors relating to Collection protocol conformance. Seems bizarre as the debug build works fine, so not sure if its a linker bug similar to SR-11564.

Duplicated keys in NonEmpty dictionary

Hi, thanks for this great library. I was just curious how you handle some conflicts for specific collections, as each collection has certain characteristics. For example, dictionary can't store duplicated keys

let zs = NonEmpty<[Int: String]>((1, "one"), [1: "one again", 3: "three"])

I think we need to have specific init extension for each kind of collection, like for Set πŸ€”

extension NonEmpty where C: SetAlgebra

Ambiguous use of operator '+'

I'm suddenly getting this issue when using Validated 0.2.1 and NonEmpty 0.3.0.

The compiler says he has these two candidates:

  public static func + <S: Sequence>(lhs: NonEmpty, rhs: S) -> NonEmpty where Element == S.Element {
    var lhs = lhs
    lhs += rhs
    return lhs
  }

  public static func + <S: Sequence>(lhs: S, rhs: NonEmpty) -> NonEmpty where Element == S.Element {
    var rhs = rhs
    rhs.insert(contentsOf: ContiguousArray(lhs), at: rhs.startIndex)
    return rhs
  }

Unexpected behaviour in MutableCollection's subscript

In the issue #1 you fixed the case:

  func testMutableCollectionWithArraySlice() {
    let numbers = Array(1...10)
    var xs = NonEmpty(0, numbers[5...])
    xs[1] = 43
    XCTAssertEqual(43, xs[1])
  }

But is it expected to have an index 1 on a NonEmpty of ArraySlice starting with 5?

`NonEmpty<NonEmpty<C>>` doesn't mean the collection has at least 2 values

I really like the safety of NonEmpty. I am writing a fully type-safe geographical library, also providing GeoJSON mapping, and I need to store arrays that have at least four values for example (see RFC 7946, section 3.1.6 for example).

I thought I'd use NonEmpty<NonEmpty<NonEmpty<NonEmpty<[MyType]>>>>, but I was surprised to se it's not how the library works. NonEmpty.swift#L9-L10 shows that the collection is stored as-is, not cut into a head value and a tail Slice.

Is it a conscious choice? Wouldn't it be semantically better to make NonEmpty behave as I thought?

No compile-time guarantee

The purpose of this package is to provide a provable, compile-time guarantee that it holds a non-empty collection. There are valid reasons why a compile-time guarantee is better than a runtime check.

Recently the implementation was changed to a runtime check without updating the readme, so now this one-liner crashes the library:

withUnsafeBytes(of: "") { $0.load(as: NonEmptyString.self).first }

All because the library no longer provides a compile-time guarantee as it claims. A previous implementation with head is resilient to those types of crashes.

I suggest either to revert the implementation or to change the description of the library.

1.0.0

How do I know when to release 1.0.0?
If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0.

SemVer.org


The release major version number is still less than 1 (right now, it's 0.2.2). By the SemVer standard, this mean that this is still in initial development and the API may change drastically between minor versions:

  1. Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

SemVer.org

Since it seems initial development is complete and this is ready for production use, can we increment the major version number to 1 (or greater) to indicate this? That would not preclude big future changes which change the functionality or public API:

  1. Major version X (X.y.z | X > 0) MUST be incremented if any backwards incompatible changes are introduced to the public API. It MAY also include minor and patch level changes. Patch and minor version MUST be reset to 0 when major version is incremented.

SemVer.org

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.