Choosing method based on type or membership?

Writing my first non-trivial Combine Publisher I found myself wanting to quickly get the min between a Subscribers.Demand and an Int. So I wrote this:

extension
Subscribers.Demand
{
	static
	func
	min(_ inDemand: Self, _ inValue: Int)
		-> Int
	{
		if inDemand == .unlimited
		{
			return inValue
		}
		
		return Swift.min(inDemand.max!, inValue)
	}
}

So far, so good. But in my Publisher code, which looks like this:

extension
Publishers
{
	final
	class
	Queueing<Upstream: Publisher>: Publisher
	{
		func
		request(_ inDemand: Subscribers.Demand)
			-> [Output]
		{
			let count = min(inDemand, self.queue.count) 
                                          ^-- error here
			let elements = Array(self.queue[..<count])
			self.queue.removeFirst(count)
			return elements
		}
	}
}

I get this error: Use of 'min' refers to instance method rather than global function 'min' in module 'Swift'. If I command-click min in Xcode, it goes nowhere, but there are a few min() methods in Combine.

Unfortunately, none of them take two arguments, and none matches the signature of my Subscriptions.Demand.min(Demand,Int) -> Int.

I don't think this is a bug, but it seems like something the compiler could unambiguously determine. Is this something I should report?

I don't think this is a bug, isn't your extension function min defined in the namespace of Subscribers.Demand and you are trying to use it — unqualified — in the namespace of Publishers.Queueing? So you'd have to write Subscribers.Demand.min(...) for it to know what you mean.

If you wrote your min as a free function outside of the extension, min(_:Subscribers.Demand, _:Int), then I think it should pick it up correctly.

2 Likes

A different approach is to remove the staticand inDemand and have it as

extension Subscribers.Demand
{
	func min( _ inValue: Int) -> Int
	{
		if self == .unlimited
		{
			return inValue
		}
		
		return Swift.min(self.max!, inValue)
	}
}

This would allow you to call it on any Subscribers.Demand as following:

extension Publishers
{
	final class Queueing<Upstream: Publisher>: Publisher
	{
		func request(_ inDemand: Subscribers.Demand) -> [Output]
		{
			let count = inDemand.min(self.queue.count) 
                                      //  ^-- new syntax here
			let elements = Array(self.queue[..<count])
			self.queue.removeFirst(count)
			return elements
		}
	}
}

That was, in fact, how I did it originally. But most uses of min are global function calls, and I thought perhaps I could treat it like binary operator definitions. But I guess not.

Yeah, I guess I was thinking about how operators can be defined as static members. Maybe they're treated differently.

Yeah, the operator behaviour is special.

1 Like
Terms of Service

Privacy Policy

Cookie Policy